Esempio n. 1
0
File: nfs.c Progetto: ouafae31/core
int VerifyMount(EvalContext *ctx, char *name, Attributes a, Promise *pp)
{
    char comm[CF_BUFSIZE], line[CF_BUFSIZE];
    FILE *pfp;
    char *host, *rmountpt, *mountpt, *opts=NULL;

    host = a.mount.mount_server;
    rmountpt = a.mount.mount_source;
    mountpt = name;

    /* Check for options required for this mount - i.e., -o ro,rsize, etc. */
    if (a.mount.mount_options)
    {
        opts = Rlist2String(a.mount.mount_options, ",");
    }
    else
    {
        opts = xstrdup(VMOUNTOPTS[VSYSTEMHARDCLASS]);
    }

    if (!DONTDO)
    {
        snprintf(comm, CF_BUFSIZE, "%s -o %s %s:%s %s", CommandArg0(VMOUNTCOMM[VSYSTEMHARDCLASS]), opts, host, rmountpt, mountpt);

        if ((pfp = cf_popen(comm, "r", true)) == NULL)
        {
            Log(LOG_LEVEL_ERR, "Failed to open pipe from %s", CommandArg0(VMOUNTCOMM[VSYSTEMHARDCLASS]));
            return 0;
        }

        ssize_t res = CfReadLine(line, CF_BUFSIZE, pfp);

        if (res == -1)
        {
            Log(LOG_LEVEL_ERR, "Unable to read output of mount command. (fread: %s)", GetErrorStr());
            cf_pclose(pfp);
            return 0;
        }

        if (res != 0 && ((strstr(line, "busy")) || (strstr(line, "Busy"))))
        {
            cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_INTERRUPTED, pp, a, "The device under %s cannot be mounted\n", mountpt);
            cf_pclose(pfp);
            return 1;
        }

        cf_pclose(pfp);
    }

    /* Since opts is either Rlist2String or xstrdup'd, we need to always free it */
    free(opts);

    cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "Mounting %s to keep promise\n", mountpt);
    return 0;
}
Esempio n. 2
0
const char *RealPackageManager(const char *manager)
{
    assert(manager);

    const char *pos = strchr(manager, ' ');
    if (strncmp(manager, "env ", 4) != 0
        && (!pos || pos - manager < 4 || strncmp(pos - 4, "/env", 4) != 0))
    {
        return CommandArg0(manager);
    }

    // Look for variable assignments.
    const char *last_pos;
    bool eq_sign_found = false;
    while (true)
    {
        if (eq_sign_found)
        {
            last_pos = pos + 1;
        }
        else
        {
            last_pos = pos + strspn(pos, " "); // Skip over consecutive spaces.
        }
        pos = strpbrk(last_pos, "= ");
        if (!pos)
        {
            break;
        }
        if (*pos == '=')
        {
            eq_sign_found = true;
        }
        else if (eq_sign_found)
        {
            eq_sign_found = false;
        }
        else
        {
            return CommandArg0(last_pos);
        }
    }

    // Reached the end? Weird. Must be env command with no real command.
    return CommandArg0(manager);
}
Esempio n. 3
0
static void VerifyProcessOp(EvalContext *ctx, Item *procdata, Attributes a, Promise *pp)
{
    int matches = 0, do_signals = true, out_of_range, killed = 0, need_to_restart = true;
    Item *killlist = NULL;

    matches = FindPidMatches(ctx, procdata, &killlist, a, pp->promiser);

/* promise based on number of matches */

    if (a.process_count.min_range != CF_NOINT)  /* if a range is specified */
    {
        if ((matches < a.process_count.min_range) || (matches > a.process_count.max_range))
        {
            cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_CHANGE, pp, a, "Process count for '%s' was out of promised range (%d found)", pp->promiser, matches);
            for (const Rlist *rp = a.process_count.out_of_range_define; rp != NULL; rp = rp->next)
            {
                if (!EvalContextHeapContainsSoft(ctx, rp->item))
                {
                    EvalContextHeapAddSoft(ctx, rp->item, PromiseGetNamespace(pp));
                }
            }
            out_of_range = true;
        }
        else
        {
            for (const Rlist *rp = a.process_count.in_range_define; rp != NULL; rp = rp->next)
            {
                if (!EvalContextHeapContainsSoft(ctx, rp->item))
                {
                    EvalContextHeapAddSoft(ctx, rp->item, PromiseGetNamespace(pp));
                }
            }
            cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Process promise for '%s' is kept", pp->promiser);
            out_of_range = false;
        }
    }
    else
    {
        out_of_range = true;
    }

    if (!out_of_range)
    {
        return;
    }

    if (a.transaction.action == cfa_warn)
    {
        do_signals = false;
    }
    else
    {
        do_signals = true;
    }

/* signal/kill promises for existing matches */

    if (do_signals && (matches > 0))
    {
        if (a.process_stop != NULL)
        {
            if (DONTDO)
            {
                cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a,
                     "Need to keep process-stop promise for '%s', but only a warning is promised", pp->promiser);
            }
            else
            {
                if (IsExecutable(CommandArg0(a.process_stop)))
                {
                    ShellCommandReturnsZero(a.process_stop, SHELL_TYPE_NONE);
                }
                else
                {
                    cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_FAIL, pp, a,
                         "Process promise to stop '%s' could not be kept because '%s' the stop operator failed",
                         pp->promiser, a.process_stop);
                    DeleteItemList(killlist);
                    return;
                }
            }
        }

        killed = DoAllSignals(ctx, killlist, a, pp);
    }

/* delegated promise to restart killed or non-existent entries */

    need_to_restart = (a.restart_class != NULL) && (killed || (matches == 0));

    DeleteItemList(killlist);

    if (!need_to_restart)
    {
        cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "No restart promised for %s", pp->promiser);
        return;
    }
    else
    {
        if (a.transaction.action == cfa_warn)
        {
            cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a,
                 "Need to keep restart promise for '%s', but only a warning is promised", pp->promiser);
        }
        else
        {
            cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "Making a one-time restart promise for '%s'", pp->promiser);
            EvalContextHeapAddSoft(ctx, a.restart_class, PromiseGetNamespace(pp));
        }
    }
}
Esempio n. 4
0
static PromiseResult VerifyProcessOp(EvalContext *ctx, Item *procdata, Attributes a, const Promise *pp)
{
    bool do_signals = true;
    int out_of_range;
    int killed = 0;
    bool need_to_restart = true;
    Item *killlist = NULL;

    int matches = FindPidMatches(procdata, &killlist, a, pp->promiser);

/* promise based on number of matches */

    PromiseResult result = PROMISE_RESULT_NOOP;
    if (a.process_count.min_range != CF_NOINT)  /* if a range is specified */
    {
        if ((matches < a.process_count.min_range) || (matches > a.process_count.max_range))
        {
            cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_CHANGE, pp, a, "Process count for '%s' was out of promised range (%d found)", pp->promiser, matches);
            result = PromiseResultUpdate(result, PROMISE_RESULT_CHANGE);
            for (const Rlist *rp = a.process_count.out_of_range_define; rp != NULL; rp = rp->next)
            {
                ClassRef ref = ClassRefParse(RlistScalarValue(rp));
                EvalContextClassPutSoft(ctx, RlistScalarValue(rp), CONTEXT_SCOPE_NAMESPACE, "source=promise");
                ClassRefDestroy(ref);
            }
            out_of_range = true;
        }
        else
        {
            for (const Rlist *rp = a.process_count.in_range_define; rp != NULL; rp = rp->next)
            {
                ClassRef ref = ClassRefParse(RlistScalarValue(rp));
                EvalContextClassPutSoft(ctx, RlistScalarValue(rp), CONTEXT_SCOPE_NAMESPACE, "source=promise");
                ClassRefDestroy(ref);
            }
            cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Process promise for '%s' is kept", pp->promiser);
            out_of_range = false;
        }
    }
    else
    {
        out_of_range = true;
    }

    if (!out_of_range)
    {
        DeleteItemList(killlist);
        return result;
    }

    if (a.transaction.action == cfa_warn)
    {
        do_signals = false;
        result = PromiseResultUpdate(result, PROMISE_RESULT_WARN);
    }
    else
    {
        do_signals = true;
    }

/* signal/kill promises for existing matches */

    if (do_signals && (matches > 0))
    {
        if (a.process_stop != NULL)
        {
            if (DONTDO)
            {
                cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a,
                     "Need to keep process-stop promise for '%s', but only a warning is promised", pp->promiser);
                result = PromiseResultUpdate(result, PROMISE_RESULT_WARN);
            }
            else
            {
                if (IsExecutable(CommandArg0(a.process_stop)))
                {
                    ShellCommandReturnsZero(a.process_stop, SHELL_TYPE_NONE);
                }
                else
                {
                    cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a,
                         "Process promise to stop '%s' could not be kept because '%s' the stop operator failed",
                         pp->promiser, a.process_stop);
                    result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL);
                    DeleteItemList(killlist);
                    return result;
                }
            }
        }

        killed = DoAllSignals(ctx, killlist, a, pp, &result);
    }

/* delegated promise to restart killed or non-existent entries */

    need_to_restart = (a.restart_class != NULL) && (killed || (matches == 0));

    DeleteItemList(killlist);

    if (!need_to_restart)
    {
        cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "No restart promised for %s", pp->promiser);
        return result;
    }
    else
    {
        if (a.transaction.action == cfa_warn)
        {
            cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a,
                 "Need to keep restart promise for '%s', but only a warning is promised", pp->promiser);
            result = PromiseResultUpdate(result, PROMISE_RESULT_WARN);
        }
        else
        {
            cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "Making a one-time restart promise for '%s'", pp->promiser);
            result = PromiseResultUpdate(result, PROMISE_RESULT_CHANGE);
            EvalContextClassPutSoft(ctx, a.restart_class, CONTEXT_SCOPE_NAMESPACE, "source=promise");
        }
    }

    return result;
}
Esempio n. 5
0
static ActionResult RepairExec(EvalContext *ctx, Attributes a, Promise *pp)
{
    char line[CF_BUFSIZE], eventname[CF_BUFSIZE];
    char cmdline[CF_BUFSIZE];
    char comm[20];
    int outsourced, count = 0;
#if !defined(__MINGW32__)
    mode_t maskval = 0;
#endif
    FILE *pfp;
    char cmdOutBuf[CF_BUFSIZE];
    int cmdOutBufPos = 0;
    int lineOutLen;

    if (IsAbsoluteFileName(CommandArg0(pp->promiser)) || a.contain.shelltype == SHELL_TYPE_NONE)
    {
        if (!IsExecutable(CommandArg0(pp->promiser)))
        {
            cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "'%s' promises to be executable but isn't", pp->promiser);

            if (strchr(pp->promiser, ' '))
            {
                Log(LOG_LEVEL_VERBOSE, "Paths with spaces must be inside escaped quoutes (e.g. \\\"%s\\\")", pp->promiser);
            }

            return ACTION_RESULT_FAILED;
        }
        else
        {
            Log(LOG_LEVEL_VERBOSE, "Promiser string contains a valid executable '%s' - ok", CommandArg0(pp->promiser));
        }
    }

    char timeout_str[CF_BUFSIZE];
    if (a.contain.timeout == CF_NOINT)
    {
        snprintf(timeout_str, CF_BUFSIZE, "no timeout");
    }
    else
    {
        snprintf(timeout_str, CF_BUFSIZE, "timeout=%ds", a.contain.timeout);
    }

    char owner_str[CF_BUFSIZE] = "";
    if (a.contain.owner != -1)
    {
        snprintf(owner_str, CF_BUFSIZE, ",uid=%ju", (uintmax_t)a.contain.owner);
    }

    char group_str[CF_BUFSIZE] = "";
    if (a.contain.group != -1)
    {
        snprintf(group_str, CF_BUFSIZE, ",gid=%ju", (uintmax_t)a.contain.group);
    }

    snprintf(cmdline, CF_BUFSIZE, "%s%s%s", pp->promiser, a.args ? " " : "", a.args ? a.args : "");

    Log(LOG_LEVEL_INFO, "Executing '%s%s%s' ... '%s'", timeout_str, owner_str, group_str, cmdline);

    BeginMeasure();

    if (DONTDO && (!a.contain.preview))
    {
        Log(LOG_LEVEL_ERR, "Would execute script '%s'", cmdline);
        return ACTION_RESULT_OK;
    }

    if (a.transaction.action != cfa_fix)
    {
        Log(LOG_LEVEL_ERR, "Command '%s' needs to be executed, but only warning was promised", cmdline);
        return ACTION_RESULT_OK;
    }

    CommandPrefix(cmdline, comm);

    if (a.transaction.background)
    {
#ifdef __MINGW32__
        outsourced = true;
#else
        Log(LOG_LEVEL_VERBOSE, "Backgrounding job '%s'", cmdline);
        outsourced = fork();
#endif
    }
    else
    {
        outsourced = false;
    }

    if (outsourced || (!a.transaction.background))    // work done here: either by child or non-background parent
    {
        if (a.contain.timeout != CF_NOINT)
        {
            SetTimeOut(a.contain.timeout);
        }

#ifndef __MINGW32__
        Log(LOG_LEVEL_VERBOSE, "(Setting umask to %jo)", (uintmax_t)a.contain.umask);
        maskval = umask(a.contain.umask);

        if (a.contain.umask == 0)
        {
            Log(LOG_LEVEL_VERBOSE, "Programming '%s' running with umask 0! Use umask= to set", cmdline);
        }
#endif /* !__MINGW32__ */

        if (a.contain.shelltype == SHELL_TYPE_POWERSHELL)
        {
#ifdef __MINGW32__
            pfp =
                cf_popen_powershell_setuid(cmdline, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot,
                                           a.transaction.background);
#else // !__MINGW32__
            Log(LOG_LEVEL_ERR, "Powershell is only supported on Windows");
            return ACTION_RESULT_FAILED;
#endif // !__MINGW32__
        }
        else if (a.contain.shelltype == SHELL_TYPE_USE)
        {
            pfp =
                cf_popen_shsetuid(cmdline, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot,
                                  a.transaction.background);
        }
        else
        {
            pfp =
                cf_popensetuid(cmdline, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot,
                               a.transaction.background);
        }

        if (pfp == NULL)
        {
            Log(LOG_LEVEL_ERR, "Couldn't open pipe to command '%s'. (cf_popen: %s)", cmdline, GetErrorStr());
            return ACTION_RESULT_FAILED;
        }

        for (;;)
        {
            ssize_t res = CfReadLine(line, CF_BUFSIZE, pfp);

            if (res == 0)
            {
                break;
            }

            if (res == -1)
            {
                Log(LOG_LEVEL_ERR, "Unable to read output from command '%s'. (fread: %s)", cmdline, GetErrorStr());
                cf_pclose(pfp);
                return ACTION_RESULT_FAILED;
            }

            if (strstr(line, "cfengine-die"))
            {
                break;
            }

            if (a.contain.preview)
            {
                PreviewProtocolLine(line, cmdline);
            }

            if (a.module)
            {
                ModuleProtocol(ctx, cmdline, line, !a.contain.nooutput, PromiseGetNamespace(pp));
            }
            else if ((!a.contain.nooutput) && (!EmptyString(line)))
            {
                lineOutLen = strlen(comm) + strlen(line) + 12;

                // if buffer is to small for this line, output it directly
                if (lineOutLen > sizeof(cmdOutBuf))
                {
                    Log(LOG_LEVEL_NOTICE, "Q: '%s': %s", comm, line);
                }
                else
                {
                    if (cmdOutBufPos + lineOutLen > sizeof(cmdOutBuf))
                    {
                        Log(LOG_LEVEL_NOTICE, "%s", cmdOutBuf);
                        cmdOutBufPos = 0;
                    }
                    sprintf(cmdOutBuf + cmdOutBufPos, "Q: \"...%s\": %s\n", comm, line);
                    cmdOutBufPos += (lineOutLen - 1);
                }
                count++;
            }
        }
#ifdef __MINGW32__
        if (outsourced)     // only get return value if we waited for command execution
        {
            cf_pclose(pfp);
        }
        else
#endif /* __MINGW32__ */
        {
            int ret = cf_pclose(pfp);

            if (ret == -1)
            {
                cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_FAIL, pp, a, "Finished script '%s' - failed (abnormal termination)", pp->promiser);
            }
            else
            {
                VerifyCommandRetcode(ctx, ret, true, a, pp);
            }
        }
    }

    if (count)
    {
        if (cmdOutBufPos)
        {
            Log(LOG_LEVEL_NOTICE, "%s", cmdOutBuf);
        }

        Log(LOG_LEVEL_INFO, "Last %d quoted lines were generated by promiser '%s'", count, cmdline);
    }

    if (a.contain.timeout != CF_NOINT)
    {
        alarm(0);
        signal(SIGALRM, SIG_DFL);
    }

    Log(LOG_LEVEL_INFO, "Completed execution of '%s'", cmdline);
#ifndef __MINGW32__
    umask(maskval);
#endif

    snprintf(eventname, CF_BUFSIZE - 1, "Exec(%s)", cmdline);

#ifndef __MINGW32__
    if ((a.transaction.background) && outsourced)
    {
        Log(LOG_LEVEL_VERBOSE, "Backgrounded command '%s' is done - exiting", cmdline);
        exit(0);
    }
#endif /* !__MINGW32__ */

    return ACTION_RESULT_OK;
}
Esempio n. 6
0
int BusyWithClassicConnection(EvalContext *ctx, ServerConnectionState *conn)
{
    time_t tloc, trem = 0;
    char recvbuffer[CF_BUFSIZE + CF_BUFEXT], check[CF_BUFSIZE];
    char sendbuffer[CF_BUFSIZE] = { 0 };
    char filename[CF_BUFSIZE], buffer[CF_BUFSIZE], args[CF_BUFSIZE], out[CF_BUFSIZE];
    long time_no_see = 0;
    unsigned int len = 0;
    int drift, plainlen, received, encrypted = 0;
    ServerFileGetState get_args;
    Item *classes;

    memset(recvbuffer, 0, CF_BUFSIZE + CF_BUFEXT);
    memset(&get_args, 0, sizeof(get_args));

    received = ReceiveTransaction(conn->conn_info, recvbuffer, NULL);
    if (received == -1 || received == 0)
    {
        return false;
    }

    if (strlen(recvbuffer) == 0)
    {
        Log(LOG_LEVEL_WARNING, "Got NULL transmission, skipping!");
        return true;
    }

    /* Don't process request if we're signalled to exit. */
    if (IsPendingTermination())
    {
        return false;
    }

    ProtocolCommandClassic command = GetCommandClassic(recvbuffer);

    switch (command)
    {
    /* Plain text authentication; this MUST be the first command client
       using classic protocol is sending. */
    case PROTOCOL_COMMAND_AUTH_PLAIN:
        SetConnectionData(conn, (char *) (recvbuffer + strlen("CAUTH ")));

        if (conn->username == NULL || IsUserNameValid(conn->username) == false)
        {
            Log(LOG_LEVEL_INFO, "Client is sending wrong username: '******'", conn->username);
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        /* This is used only for forcing correct state of state machine while
           connecting and authenticating user using classic protocol. */
        conn->user_data_set = true;

        return true;

    /* This MUST be exactly second command client using classic protocol is sending.
       This is where key agreement takes place. */
    case PROTOCOL_COMMAND_AUTH_SECURE:
        /* First command was ommited by client; this is protocol violation. */
        if (!conn->user_data_set)
        {
            Log(LOG_LEVEL_INFO, "Client is not verified; rejecting connection");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        conn->rsa_auth = AuthenticationDialogue(conn, recvbuffer, received);
        if (!conn->rsa_auth)
        {
            Log(LOG_LEVEL_INFO, "Auth dialogue error");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        return true;
    default:
        break;
    }

    /* At this point we should have both user_data_set and rsa_auth set to perform any operation.
       We can check only for second one as without first it won't be set up. */
    if (!conn->rsa_auth)
    {
        Log(LOG_LEVEL_INFO, "Server refusal due to no RSA authentication [command: %d]", command);
        RefuseAccess(conn, recvbuffer);
        return false;
    }

    /* We have to have key at this point. */
    assert(conn->session_key);

    /* At this point we can safely do next switch and make sure user is authenticated. */
    switch (command)
    {
    case PROTOCOL_COMMAND_EXEC:
        memset(args, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "EXEC %255[^\n]", args);

        if (!AllowedUser(conn->username))
        {
            Log(LOG_LEVEL_INFO, "Server refusal due to non-allowed user");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        if (!AccessControl(ctx, CommandArg0(CFRUNCOMMAND), conn, false))
        {
            Log(LOG_LEVEL_INFO, "Server refusal due to denied access to requested object");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        if (!MatchClasses(ctx, conn))
        {
            Log(LOG_LEVEL_INFO, "Server refusal due to failed class/context match");
            Terminate(conn->conn_info);
            return false;
        }

        DoExec(ctx, conn, args);
        Terminate(conn->conn_info);
        return false;

    case PROTOCOL_COMMAND_VERSION:
        snprintf(sendbuffer, sizeof(sendbuffer), "OK: %s", Version());
        SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
        return conn->user_data_set;

    case PROTOCOL_COMMAND_GET:
        memset(filename, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "GET %d %[^\n]", &(get_args.buf_size), filename);

        if ((get_args.buf_size < 0) || (get_args.buf_size > CF_BUFSIZE))
        {
            Log(LOG_LEVEL_INFO, "GET buffer out of bounds");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        if (!AccessControl(ctx, filename, conn, false))
        {
            Log(LOG_LEVEL_INFO, "Access denied to get object");
            RefuseAccess(conn, recvbuffer);
            return true;
        }

        memset(sendbuffer, 0, sizeof(sendbuffer));

        if (get_args.buf_size >= CF_BUFSIZE)
        {
            get_args.buf_size = 2048;
        }

        get_args.connect = conn;
        get_args.encrypt = false;
        get_args.replybuff = sendbuffer;
        get_args.replyfile = filename;

        CfGetFile(&get_args);

        return true;

    case PROTOCOL_COMMAND_GET_SECURE:
        memset(buffer, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "SGET %u %d", &len, &(get_args.buf_size));

        if (received != len + CF_PROTO_OFFSET)
        {
            Log(LOG_LEVEL_VERBOSE, "Protocol error SGET");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        plainlen = DecryptString(conn->encryption_type, recvbuffer + CF_PROTO_OFFSET, buffer, conn->session_key, len);

        cfscanf(buffer, strlen("GET"), strlen("dummykey"), check, sendbuffer, filename);

        if (strcmp(check, "GET") != 0)
        {
            Log(LOG_LEVEL_INFO, "SGET/GET problem");
            RefuseAccess(conn, recvbuffer);
            return true;
        }

        if ((get_args.buf_size < 0) || (get_args.buf_size > 8192))
        {
            Log(LOG_LEVEL_INFO, "SGET bounding error");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        if (get_args.buf_size >= CF_BUFSIZE)
        {
            get_args.buf_size = 2048;
        }

        Log(LOG_LEVEL_DEBUG, "Confirm decryption, and thus validity of caller");
        Log(LOG_LEVEL_DEBUG, "SGET '%s' with blocksize %d", filename, get_args.buf_size);

        if (!AccessControl(ctx, filename, conn, true))
        {
            Log(LOG_LEVEL_INFO, "Access control error");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        memset(sendbuffer, 0, sizeof(sendbuffer));

        get_args.connect = conn;
        get_args.encrypt = true;
        get_args.replybuff = sendbuffer;
        get_args.replyfile = filename;

        CfEncryptGetFile(&get_args);
        return true;

    case PROTOCOL_COMMAND_OPENDIR_SECURE:
        memset(buffer, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "SOPENDIR %u", &len);

        if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET)))
        {
            Log(LOG_LEVEL_VERBOSE, "Protocol error OPENDIR: %d", len);
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        memcpy(out, recvbuffer + CF_PROTO_OFFSET, len);

        plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len);

        if (strncmp(recvbuffer, "OPENDIR", 7) != 0)
        {
            Log(LOG_LEVEL_INFO, "Opendir failed to decrypt");
            RefuseAccess(conn, recvbuffer);
            return true;
        }

        memset(filename, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "OPENDIR %[^\n]", filename);

        if (!AccessControl(ctx, filename, conn, true))        /* opendir don't care about privacy */
        {
            Log(LOG_LEVEL_INFO, "Access error");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        CfSecOpenDirectory(conn, sendbuffer, filename);
        return true;

    case PROTOCOL_COMMAND_OPENDIR:
        memset(filename, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "OPENDIR %[^\n]", filename);

        if (!AccessControl(ctx, filename, conn, true))        /* opendir don't care about privacy */
        {
            Log(LOG_LEVEL_INFO, "DIR access error");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        CfOpenDirectory(conn, sendbuffer, filename);
        return true;

    case PROTOCOL_COMMAND_SYNC_SECURE:
        memset(buffer, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "SSYNCH %u", &len);

        if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET)))
        {
            Log(LOG_LEVEL_VERBOSE, "Protocol error SSYNCH: %d", len);
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        memcpy(out, recvbuffer + CF_PROTO_OFFSET, len);

        plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len);

        if (plainlen < 0)
        {
            DebugBinOut((char *) conn->session_key, 32, "Session key");
            Log(LOG_LEVEL_ERR, "Bad decrypt (%d)", len);
        }

        if (strncmp(recvbuffer, "SYNCH", 5) != 0)
        {
            Log(LOG_LEVEL_INFO, "No synch");
            RefuseAccess(conn, recvbuffer);
            return true;
        }

        /* roll through, no break */

    case PROTOCOL_COMMAND_SYNC:
        memset(filename, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "SYNCH %ld STAT %[^\n]", &time_no_see, filename);

        trem = (time_t) time_no_see;

        if ((time_no_see == 0) || (filename[0] == '\0'))
        {
            break;
        }

        if ((tloc = time((time_t *) NULL)) == -1)
        {
            Log(LOG_LEVEL_INFO, "Couldn't read system clock. (time: %s)", GetErrorStr());
            SendTransaction(conn->conn_info, "BAD: clocks out of synch", 0, CF_DONE);
            return true;
        }

        drift = (int) (tloc - trem);

        if (!AccessControl(ctx, filename, conn, true))
        {
            Log(LOG_LEVEL_INFO, "Access control in sync");
            RefuseAccess(conn, recvbuffer);
            return true;
        }

        if (DENYBADCLOCKS && (drift * drift > CLOCK_DRIFT * CLOCK_DRIFT))
        {
            snprintf(sendbuffer, sizeof(sendbuffer),
                     "BAD: Clocks are too far unsynchronized %ld/%ld",
                     (long) tloc, (long) trem);
            SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
            return true;
        }
        else
        {
            Log(LOG_LEVEL_DEBUG, "Clocks were off by %ld", (long) tloc - (long) trem);
            StatFile(conn, sendbuffer, filename);
        }

        return true;

    case PROTOCOL_COMMAND_MD5_SECURE:
        sscanf(recvbuffer, "SMD5 %u", &len);

        if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET)))
        {
            Log(LOG_LEVEL_INFO, "Decryption error");
            RefuseAccess(conn, recvbuffer);
            return true;
        }

        memcpy(out, recvbuffer + CF_PROTO_OFFSET, len);
        plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len);

        if (strncmp(recvbuffer, "MD5", 3) != 0)
        {
            Log(LOG_LEVEL_INFO, "MD5 protocol error");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        /* roll through, no break */

    case PROTOCOL_COMMAND_MD5:
        CompareLocalHash(conn, sendbuffer, recvbuffer);
        return true;

    case PROTOCOL_COMMAND_VAR_SECURE:
        sscanf(recvbuffer, "SVAR %u", &len);

        if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET)))
        {
            Log(LOG_LEVEL_INFO, "Decrypt error SVAR");
            RefuseAccess(conn, "decrypt error SVAR");
            return true;
        }

        memcpy(out, recvbuffer + CF_PROTO_OFFSET, len);
        plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len);
        encrypted = true;

        if (strncmp(recvbuffer, "VAR", 3) != 0)
        {
            Log(LOG_LEVEL_INFO, "VAR protocol defect");
            RefuseAccess(conn, "decryption failure");
            return false;
        }

        /* roll through, no break */

    case PROTOCOL_COMMAND_VAR:
        if (!LiteralAccessControl(ctx, recvbuffer, conn, encrypted))
        {
            Log(LOG_LEVEL_INFO, "Literal access failure");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        GetServerLiteral(ctx, conn, sendbuffer, recvbuffer, encrypted);
        return true;

    case PROTOCOL_COMMAND_CONTEXT_SECURE:
        sscanf(recvbuffer, "SCONTEXT %u", &len);

        if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET)))
        {
            Log(LOG_LEVEL_INFO, "Decrypt error SCONTEXT, len,received = %d,%d", len, received);
            RefuseAccess(conn, "decrypt error SCONTEXT");
            return true;
        }

        memcpy(out, recvbuffer + CF_PROTO_OFFSET, len);
        plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len);
        encrypted = true;

        if (strncmp(recvbuffer, "CONTEXT", 7) != 0)
        {
            Log(LOG_LEVEL_INFO, "CONTEXT protocol defect...");
            RefuseAccess(conn, "Decryption failed?");
            return false;
        }

        /* roll through, no break */

    case PROTOCOL_COMMAND_CONTEXT:
        if ((classes = ContextAccessControl(ctx, recvbuffer, conn, encrypted)) == NULL)
        {
            Log(LOG_LEVEL_INFO, "Context access failure on %s", recvbuffer);
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        ReplyServerContext(conn, encrypted, classes);
        return true;

    case PROTOCOL_COMMAND_QUERY_SECURE:
        sscanf(recvbuffer, "SQUERY %u", &len);

        if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET)))
        {
            Log(LOG_LEVEL_INFO, "Decrypt error SQUERY");
            RefuseAccess(conn, "decrypt error SQUERY");
            return true;
        }

        memcpy(out, recvbuffer + CF_PROTO_OFFSET, len);
        plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len);

        if (strncmp(recvbuffer, "QUERY", 5) != 0)
        {
            Log(LOG_LEVEL_INFO, "QUERY protocol defect");
            RefuseAccess(conn, "decryption failure");
            return false;
        }

        if (!LiteralAccessControl(ctx, recvbuffer, conn, true))
        {
            Log(LOG_LEVEL_INFO, "Query access failure");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        if (GetServerQuery(conn, recvbuffer, true))       /* always encrypt */
        {
            return true;
        }

        break;

    case PROTOCOL_COMMAND_CALL_ME_BACK:
        sscanf(recvbuffer, "SCALLBACK %u", &len);

        if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET)))
        {
            Log(LOG_LEVEL_INFO, "Decrypt error CALL_ME_BACK");
            RefuseAccess(conn, "decrypt error CALL_ME_BACK");
            return true;
        }

        memcpy(out, recvbuffer + CF_PROTO_OFFSET, len);
        plainlen = DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len);

        if (strncmp(recvbuffer, "CALL_ME_BACK collect_calls", strlen("CALL_ME_BACK collect_calls")) != 0)
        {
            Log(LOG_LEVEL_INFO, "CALL_ME_BACK protocol defect");
            RefuseAccess(conn, "decryption failure");
            return false;
        }

        if (!LiteralAccessControl(ctx, recvbuffer, conn, true))
        {
            Log(LOG_LEVEL_INFO, "Query access failure");
            RefuseAccess(conn, recvbuffer);
            return false;
        }

        if (ReceiveCollectCall(conn))
        {
            return true;
        }

    case PROTOCOL_COMMAND_AUTH_PLAIN:
    case PROTOCOL_COMMAND_AUTH_SECURE:
    case PROTOCOL_COMMAND_AUTH:
    case PROTOCOL_COMMAND_CONTEXTS:
    case PROTOCOL_COMMAND_BAD:
        Log(LOG_LEVEL_WARNING, "Unexpected protocol command");
    }

    strcpy(sendbuffer, "BAD: Request denied");
    SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
    Log(LOG_LEVEL_INFO, "Closing connection, due to request: '%s'", recvbuffer);
    return false;
}
Esempio n. 7
0
PromiseResult VerifyMount(EvalContext *ctx, char *name, const Attributes *a, const Promise *pp)
{
    char comm[CF_BUFSIZE];
    FILE *pfp;
    char *host, *rmountpt, *mountpt, *opts=NULL;

    host = a->mount.mount_server;
    rmountpt = a->mount.mount_source;
    mountpt = name;

    /* Check for options required for this mount - i.e., -o ro,rsize, etc. */
    if (a->mount.mount_options)
    {
        opts = Rlist2String(a->mount.mount_options, ",");
    }
    else
    {
        opts = xstrdup(VMOUNTOPTS[VSYSTEMHARDCLASS]);
    }

    PromiseResult result = PROMISE_RESULT_NOOP;
    if (!DONTDO)
    {
        snprintf(comm, CF_BUFSIZE, "%s -o %s %s:%s %s", CommandArg0(VMOUNTCOMM[VSYSTEMHARDCLASS]), opts, host, rmountpt, mountpt);

        if ((pfp = cf_popen(comm, "r", true)) == NULL)
        {
            Log(LOG_LEVEL_ERR, "Failed to open pipe from '%s'", CommandArg0(VMOUNTCOMM[VSYSTEMHARDCLASS]));
            return PROMISE_RESULT_FAIL;
        }

        size_t line_size = CF_BUFSIZE;
        char *line = xmalloc(line_size);

        ssize_t res = CfReadLine(&line, &line_size, pfp);

        if (res == -1)
        {
            if (!feof(pfp))
            {
                Log(LOG_LEVEL_ERR, "Unable to read output of mount command. (fread: %s)", GetErrorStr());
                cf_pclose(pfp);
                free(line);
                return PROMISE_RESULT_FAIL;
            }
        }
        else if ((strstr(line, "busy")) || (strstr(line, "Busy")))
        {
            cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_INTERRUPTED, pp, a, "The device under '%s' cannot be mounted", mountpt);
            result = PromiseResultUpdate(result, PROMISE_RESULT_INTERRUPTED);
            cf_pclose(pfp);
            free(line);
            return 1;
        }

        free(line);
        cf_pclose(pfp);
    }

    /* Since opts is either Rlist2String or xstrdup'd, we need to always free it */
    free(opts);

    cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "Mounting '%s' to keep promise", mountpt);
    result = PromiseResultUpdate(result, PROMISE_RESULT_CHANGE);

    return result;
}
Esempio n. 8
0
static void test_command_promiser(void **state)
{
    char *t1 = "/bin/echo";
    assert_string_equal(CommandArg0(t1), "/bin/echo");

    char *t2 = "/bin/rpm -qa --queryformat \"i | repos | %{name} | %{version}-%{release} | %{arch}\n\"";
    assert_string_equal(CommandArg0(t2), "/bin/rpm");
    
    char *t3 = "/bin/mount -va";
    assert_string_equal(CommandArg0(t3), "/bin/mount");

    char *t4 = "\"/bin/echo\"";
    assert_string_equal(CommandArg0(t4), "/bin/echo");
    
    char *t5 = "\"/bin/echo\" 123";
    assert_string_equal(CommandArg0(t5), "/bin/echo");

    char *t6 = "\"/bin/echo with space\" 123";
    assert_string_equal(CommandArg0(t6), "/bin/echo with space");

    char *t7 = "c:\\Windows\\System32\\cmd.exe";
    assert_string_equal(CommandArg0(t7), "c:\\Windows\\System32\\cmd.exe");

    char *t8 = "\"c:\\Windows\\System32\\cmd.exe\"";
    assert_string_equal(CommandArg0(t8), "c:\\Windows\\System32\\cmd.exe");

    char *t9 = "\"c:\\Windows\\System32\\cmd.exe\" /some args here";
    assert_string_equal(CommandArg0(t9), "c:\\Windows\\System32\\cmd.exe");

    char *t10 = "\"c:\\Windows\\System32 with space\\cmd.exe\"";
    assert_string_equal(CommandArg0(t10), "c:\\Windows\\System32 with space\\cmd.exe");

    char *t11 = "\"c:\\Windows\\System32 with space\\cmd.exe\" /some args here";
    assert_string_equal(CommandArg0(t11), "c:\\Windows\\System32 with space\\cmd.exe");

    char *t12 = "\"c:\\Windows\\System32 with space\\cmd.exe\" /some \"args here\"";
    assert_string_equal(CommandArg0(t12), "c:\\Windows\\System32 with space\\cmd.exe");

    char *t13 = "\\\\mycommand";
    assert_string_equal(CommandArg0(t13), "\\\\mycommand");

    char *t14 = "\\\\myhost\\share\\command.exe";
    assert_string_equal(CommandArg0(t14), "\\\\myhost\\share\\command.exe");

    char *t15 = "\"\\\\myhost\\share\\command.exe\"";
    assert_string_equal(CommandArg0(t15), "\\\\myhost\\share\\command.exe");


    /* bad input */

    char *b1 = "\"/bin/echo 123";
    assert_string_equal(CommandArg0(b1), "/bin/echo 123");

    char *b2 = "/bin/echo\" 123";
    assert_string_equal(CommandArg0(b2), "/bin/echo\"");

    char *b3 = "";
    assert_string_equal(CommandArg0(b3), "");
    
}
Esempio n. 9
0
bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn)
{
    time_t tloc, trem = 0;
    char recvbuffer[CF_BUFSIZE + CF_BUFEXT], sendbuffer[CF_BUFSIZE];
    char filename[CF_BUFSIZE], args[CF_BUFSIZE], out[CF_BUFSIZE];
    long time_no_see = 0;
    unsigned int len = 0;
    int drift, received;
    ServerFileGetState get_args;
    Item *classes;

    /* We never double encrypt within the TLS layer */
    const int encrypted = 0;


    memset(recvbuffer, 0, CF_BUFSIZE + CF_BUFEXT);
    memset(&get_args, 0, sizeof(get_args));

    received = ReceiveTransaction(&conn->conn_info, recvbuffer, NULL);
    if (received == -1 || received == 0)
    {
        return false;
    }

    if (strlen(recvbuffer) == 0)
    {
        Log(LOG_LEVEL_WARNING, "Got NULL transmission, skipping!");
        return true;
    }

    /* Don't process request if we're signalled to exit. */
    if (IsPendingTermination())
    {
        return false;
    }

    switch (GetCommandNew(recvbuffer))
    {
    case PROTOCOL_COMMAND_EXEC:
        memset(args, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "EXEC %255[^\n]", args);

        if (!conn->id_verified)
        {
            Log(LOG_LEVEL_INFO, "Server refusal due to incorrect identity");
            RefuseAccess(conn, 0, recvbuffer);
            return false;
        }

        if (!AllowedUser(conn->username))
        {
            Log(LOG_LEVEL_INFO, "Server refusal due to non-allowed user");
            RefuseAccess(conn, 0, recvbuffer);
            return false;
        }

        if (!conn->rsa_auth)
        {
            Log(LOG_LEVEL_INFO, "Server refusal due to no RSA authentication");
            RefuseAccess(conn, 0, recvbuffer);
            return false;
        }

        if (!AccessControl(ctx, CommandArg0(CFRUNCOMMAND), conn, false))
        {
            Log(LOG_LEVEL_INFO, "Server refusal due to denied access to requested object");
            RefuseAccess(conn, 0, recvbuffer);
            return false;
        }

        if (!MatchClasses(ctx, conn))
        {
            Log(LOG_LEVEL_INFO, "Server refusal due to failed class/context match");
            Terminate(&conn->conn_info);
            return false;
        }

        DoExec(ctx, conn, args);
        Terminate(&conn->conn_info);
        return false;

    case PROTOCOL_COMMAND_VERSION:

        if (!conn->id_verified)
        {
            Log(LOG_LEVEL_INFO, "ID not verified");
            RefuseAccess(conn, 0, recvbuffer);
        }

        snprintf(conn->output, CF_BUFSIZE, "OK: %s", Version());
        SendTransaction(&conn->conn_info, conn->output, 0, CF_DONE);
        return conn->id_verified;

    case PROTOCOL_COMMAND_GET:

        memset(filename, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "GET %d %[^\n]", &(get_args.buf_size), filename);

        if ((get_args.buf_size < 0) || (get_args.buf_size > CF_BUFSIZE))
        {
            Log(LOG_LEVEL_INFO, "GET buffer out of bounds");
            RefuseAccess(conn, 0, recvbuffer);
            return false;
        }

        if (!conn->id_verified)
        {
            Log(LOG_LEVEL_INFO, "ID not verified");
            RefuseAccess(conn, 0, recvbuffer);
            return false;
        }

        if (!AccessControl(ctx, filename, conn, false))
        {
            Log(LOG_LEVEL_INFO, "Access denied to get object");
            RefuseAccess(conn, 0, recvbuffer);
            return true;
        }

        memset(sendbuffer, 0, CF_BUFSIZE);

        if (get_args.buf_size >= CF_BUFSIZE)
        {
            get_args.buf_size = 2048;
        }

        get_args.connect = conn;
        get_args.encrypt = false;
        get_args.replybuff = sendbuffer;
        get_args.replyfile = filename;

        CfGetFile(&get_args);

        return true;

    case PROTOCOL_COMMAND_OPENDIR:

        memset(filename, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "OPENDIR %[^\n]", filename);

        if (!conn->id_verified)
        {
            Log(LOG_LEVEL_INFO, "ID not verified");
            RefuseAccess(conn, 0, recvbuffer);
            return false;
        }

        if (!AccessControl(ctx, filename, conn, true))        /* opendir don't care about privacy */
        {
            Log(LOG_LEVEL_INFO, "DIR access error");
            RefuseAccess(conn, 0, recvbuffer);
            return false;
        }

        CfOpenDirectory(conn, sendbuffer, filename);
        return true;

    case PROTOCOL_COMMAND_SYNC:

        if (!conn->id_verified)
        {
            Log(LOG_LEVEL_INFO, "ID not verified");
            RefuseAccess(conn, 0, recvbuffer);
            return false;
        }

        memset(filename, 0, CF_BUFSIZE);
        sscanf(recvbuffer, "SYNCH %ld STAT %[^\n]", &time_no_see, filename);

        trem = (time_t) time_no_see;

        if ((time_no_see == 0) || (filename[0] == '\0'))
        {
            break;
        }

        if ((tloc = time((time_t *) NULL)) == -1)
        {
            sprintf(conn->output, "Couldn't read system clock\n");
            Log(LOG_LEVEL_INFO, "Couldn't read system clock. (time: %s)", GetErrorStr());
            SendTransaction(&conn->conn_info, "BAD: clocks out of synch", 0, CF_DONE);
            return true;
        }

        drift = (int) (tloc - trem);

        if (!AccessControl(ctx, filename, conn, true))
        {
            Log(LOG_LEVEL_VERBOSE, "AccessControl: access denied");
            RefuseAccess(conn, 0, recvbuffer);
            return true;
        }

        if (DENYBADCLOCKS && (drift * drift > CLOCK_DRIFT * CLOCK_DRIFT))
        {
            snprintf(conn->output, CF_BUFSIZE - 1, "BAD: Clocks are too far unsynchronized %ld/%ld\n", (long) tloc,
                     (long) trem);
            SendTransaction(&conn->conn_info, conn->output, 0, CF_DONE);
            return true;
        }
        else
        {
            Log(LOG_LEVEL_DEBUG, "Clocks were off by %ld", (long) tloc - (long) trem);
            StatFile(conn, sendbuffer, filename);
        }

        return true;

    case PROTOCOL_COMMAND_MD5:

        if (!conn->id_verified)
        {
            Log(LOG_LEVEL_INFO, "ID not verified");
            RefuseAccess(conn, 0, recvbuffer);
            return true;
        }

        CompareLocalHash(conn, sendbuffer, recvbuffer);
        return true;

    case PROTOCOL_COMMAND_VAR:

        if (!conn->id_verified)
        {
            Log(LOG_LEVEL_INFO, "ID not verified");
            RefuseAccess(conn, 0, recvbuffer);
            return true;
        }

        if (!LiteralAccessControl(ctx, recvbuffer, conn, encrypted))
        {
            Log(LOG_LEVEL_INFO, "Literal access failure");
            RefuseAccess(conn, 0, recvbuffer);
            return false;
        }

        GetServerLiteral(ctx, conn, sendbuffer, recvbuffer, encrypted);
        return true;

    case PROTOCOL_COMMAND_CONTEXT:

        if (!conn->id_verified)
        {
            Log(LOG_LEVEL_INFO, "ID not verified");
            RefuseAccess(conn, 0, "Context probe");
            return true;
        }

        if ((classes = ContextAccessControl(ctx, recvbuffer, conn, encrypted)) == NULL)
        {
            Log(LOG_LEVEL_INFO, "Context access failure on %s", recvbuffer);
            RefuseAccess(conn, 0, recvbuffer);
            return false;
        }

        ReplyServerContext(conn, encrypted, classes);
        return true;

    case PROTOCOL_COMMAND_QUERY:

        if (!conn->id_verified)
        {
            Log(LOG_LEVEL_INFO, "ID not verified");
            RefuseAccess(conn, 0, recvbuffer);
            return true;
        }

        if (!LiteralAccessControl(ctx, recvbuffer, conn, encrypted))
        {
            Log(LOG_LEVEL_INFO, "Query access failure");
            RefuseAccess(conn, 0, recvbuffer);
            return false;
        }

        if (GetServerQuery(conn, recvbuffer, encrypted))
        {
            return true;
        }

        break;

    case PROTOCOL_COMMAND_CALL_ME_BACK:

        sscanf(recvbuffer, "SCALLBACK %u", &len);

        if ((len >= sizeof(out)) || (received != (len + CF_PROTO_OFFSET)))
        {
            Log(LOG_LEVEL_INFO, "Decrypt error CALL_ME_BACK");
            RefuseAccess(conn, 0, "decrypt error CALL_ME_BACK");
            return true;
        }

        memcpy(out, recvbuffer + CF_PROTO_OFFSET, len);
        DecryptString(conn->encryption_type, out, recvbuffer, conn->session_key, len);

        if (strncmp(recvbuffer, "CALL_ME_BACK collect_calls", strlen("CALL_ME_BACK collect_calls")) != 0)
        {
            Log(LOG_LEVEL_INFO, "CALL_ME_BACK protocol defect");
            RefuseAccess(conn, 0, "decryption failure");
            return false;
        }

        if (!conn->id_verified)
        {
            Log(LOG_LEVEL_INFO, "ID not verified");
            RefuseAccess(conn, 0, recvbuffer);
            return true;
        }

        if (!LiteralAccessControl(ctx, recvbuffer, conn, true))
        {
            Log(LOG_LEVEL_INFO, "Query access failure");
            RefuseAccess(conn, 0, recvbuffer);
            return false;
        }
        return ReceiveCollectCall(conn);

    case PROTOCOL_COMMAND_BAD:
        Log(LOG_LEVEL_WARNING, "Unexpected protocol command: %s", recvbuffer);
    }

    sprintf(sendbuffer, "BAD: Request denied\n");
    SendTransaction(&conn->conn_info, sendbuffer, 0, CF_DONE);
    Log(LOG_LEVEL_INFO, "Closing connection, due to request: '%s'", recvbuffer);
    return false;
}
Esempio n. 10
0
static Item *MonReSample(EvalContext *ctx, int slot, Attributes a, const Promise *pp, PromiseResult *result)
{
 CfLock thislock;
 char eventname[CF_BUFSIZE];
 char comm[20];
 struct timespec start;
 FILE *fin = NULL;
 mode_t maskval = 0;
 
 if (a.measure.stream_type && strcmp(a.measure.stream_type, "pipe") == 0)
    {
    if (!IsExecutable(CommandArg0(pp->promiser)))
       {
       cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "%s promises to be executable but isn't\n", pp->promiser);
       *result = PromiseResultUpdate(*result, PROMISE_RESULT_FAIL);
       return NULL;
       }
    else
       {
       Log(LOG_LEVEL_VERBOSE, "Promiser string contains a valid executable (%s) - ok", CommandArg0(pp->promiser));
       }
    }
 
 TransactionContext tc =
     {
     .expireafter = a.transaction.expireafter,
     .ifelapsed = MONITOR_RESTARTED ? 0 : a.transaction.ifelapsed, // Force a measurement if restarted
     };
 
 CFSTARTTIME = time(NULL);
 
 thislock = AcquireLock(ctx, pp->promiser, VUQNAME, CFSTARTTIME, tc, pp, false);
 
 if (thislock.lock == NULL)
    {
    if (a.measure.history_type && (strcmp(a.measure.history_type, "log") == 0))
       {
       DeleteItemList(ENTERPRISE_DATA[slot].output);
       ENTERPRISE_DATA[slot].output = NULL;
       }
    else
       {
       /* If static or time-series, and too soon or busy then use a cached value
          to avoid artificial gaps in the history */
       }
    
    MONITOR_RESTARTED = false;
    return ENTERPRISE_DATA[slot].output;
    }
 else
    {
    DeleteItemList(ENTERPRISE_DATA[slot].output);
    ENTERPRISE_DATA[slot].output = NULL;
    
    Log(LOG_LEVEL_INFO, "Sampling \'%s\' ...(timeout=%d,owner=%ju,group=%ju)", pp->promiser, a.contain.timeout,
        (uintmax_t)a.contain.owner, (uintmax_t)a.contain.group);
    
    start = BeginMeasure();
    
    CommandPrefix(pp->promiser, comm);
    
    if (a.contain.timeout != 0)
       {
       SetTimeOut(a.contain.timeout);
       }
    
    /* Stream types */
    
    if (a.measure.stream_type && strcmp(a.measure.stream_type, "file") == 0)
       {
       long filepos = 0;
       struct stat sb;
       
       Log(LOG_LEVEL_VERBOSE, "Stream \"%s\" is a plain file", pp->promiser);
       
       if (stat(pp->promiser, &sb) == -1)
          {
          Log(LOG_LEVEL_INFO, "Unable to find stream '%s'. (stat: %s)", pp->promiser, GetErrorStr());
          YieldCurrentLock(thislock);
          MONITOR_RESTARTED = false;
          return NULL;
          }

       fin = safe_fopen(pp->promiser, "r");
       
       if (a.measure.growing)
          {
          filepos = Mon_RestoreFilePosition(pp->promiser);
          
          if (sb.st_size >= filepos)
             {
             fseek(fin, filepos, SEEK_SET);
             }
          }
       }
    else if (a.measure.stream_type && strcmp(a.measure.stream_type, "pipe") == 0)
       {
       Log(LOG_LEVEL_VERBOSE, "(Setting pipe umask to %jo)", (uintmax_t)a.contain.umask);
       maskval = umask(a.contain.umask);
       
       if (a.contain.umask == 0)
          {
          Log(LOG_LEVEL_VERBOSE, "Programming %s running with umask 0! Use umask= to set", pp->promiser);
          }
       
       
       // Mark: This is strange that we used these wrappers. Currently no way of setting these
       a.contain.owner = -1;
       a.contain.group = -1;
       a.contain.chdir = NULL;
       a.contain.chroot = NULL;
       // Mark: they were unset, and would fail for non-root(!)
       
       if (a.contain.shelltype == SHELL_TYPE_POWERSHELL)
          {
#ifdef __MINGW32__
          fin =
              cf_popen_powershell_setuid(pp->promiser, "r", a.contain.owner, a.contain.group, a.contain.chdir,
                                         a.contain.chroot, false);
#else // !__MINGW32__
          Log(LOG_LEVEL_ERR, "Powershell is only supported on Windows");
          YieldCurrentLock(thislock);
          MONITOR_RESTARTED = false;
          return NULL;
#endif // !__MINGW32__
          }
       else if (a.contain.shelltype == SHELL_TYPE_USE)
          {
          fin = cf_popen_shsetuid(pp->promiser, "r", a.contain.owner, a.contain.group, a.contain.chdir, a.contain.chroot, false);
          }
       else
          {
          fin =
              cf_popensetuid(pp->promiser, "r", a.contain.owner, a.contain.group, a.contain.chdir,
                             a.contain.chroot, false);
          }
       }
    
    /* generic file stream */
    
    if (fin == NULL)
       {
       cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a,
            "Couldn't open pipe to command '%s'. (cf_popen: %s)", pp->promiser, GetErrorStr());
       *result = PromiseResultUpdate(*result, PROMISE_RESULT_FAIL);
       YieldCurrentLock(thislock);
       MONITOR_RESTARTED = false;
       return ENTERPRISE_DATA[slot].output;
       }
    
    size_t line_size = CF_BUFSIZE;
    char *line = xmalloc(line_size);
    
    for (;;)
       {
       ssize_t res = CfReadLine(&line, &line_size, fin);
       if (res == -1)
          {
          if (!feof(fin))
             {
             cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_TIMEOUT, pp, a, "Sample stream '%s'. (fread: %s)",
                  pp->promiser, GetErrorStr());
             *result = PromiseResultUpdate(*result, PROMISE_RESULT_TIMEOUT);
             YieldCurrentLock(thislock);
             free(line);
             return ENTERPRISE_DATA[slot].output;
             }
          else
             {
             break;
             }
          }
       
       AppendItem(&(ENTERPRISE_DATA[slot].output), line, NULL);
       }
    
    free(line);
    
    if (a.measure.stream_type && strcmp(a.measure.stream_type, "file") == 0)
       {
       long fileptr = ftell(fin);
       
       fclose(fin);
       Mon_SaveFilePosition(pp->promiser, fileptr);
       }
    else if (a.measure.stream_type && strcmp(a.measure.stream_type, "pipe") == 0)
       {
       cf_pclose(fin);
       }
    }
 
 if (a.contain.timeout != 0)
    {
    alarm(0);
    signal(SIGALRM, SIG_DFL);
    }
 
 Log(LOG_LEVEL_INFO, "Collected sample of %s", pp->promiser);
 umask(maskval);
 YieldCurrentLock(thislock);
 MONITOR_RESTARTED = false;
 
 snprintf(eventname, CF_BUFSIZE - 1, "Sample(%s)", pp->promiser);
 EndMeasure(eventname, start);
 return ENTERPRISE_DATA[slot].output;
}

/************************************************************************************/

void HistoryUpdate(EvalContext *ctx, Averages newvals)
{
 CfLock thislock;
 time_t now = time(NULL);
 
/* We do this only once per hour - this should not be changed */
 
 Log(LOG_LEVEL_VERBOSE, "(Updating long-term history database)");
 
 Policy *history_db_policy = PolicyNew();
 Promise *pp = NULL;
 Bundle *bp = PolicyAppendBundle(history_db_policy, NamespaceDefault(), "history_db_bundle", "agent", NULL, NULL);
 PromiseType *tp = BundleAppendPromiseType(bp, "history_db");
 
 pp = PromiseTypeAppendPromise(tp, "the long term memory", (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, NULL);
 assert(pp);
 
 TransactionContext tc =
     {
         .expireafter = 0,
         .ifelapsed = 59
     };
 
 thislock = AcquireLock(ctx, pp->promiser, VUQNAME, now, tc, pp, false);
 
 if (thislock.lock == NULL)
    {
    PolicyDestroy(history_db_policy);
    return;
    }
 
/* Refresh the class context of the agent */
 
 EvalContextClear(ctx);
 
 DetectEnvironment(ctx);
 time_t t = SetReferenceTime();
 UpdateTimeClasses(ctx, t);
 
 EvalContextHeapPersistentLoadAll(ctx);
 LoadSystemConstants(ctx);
 
 YieldCurrentLock(thislock);
 PolicyDestroy(history_db_policy);
 
 Mon_HistoryUpdate(CFSTARTTIME, &newvals); 
 Mon_DumpSlowlyVaryingObservations();

}

/************************************************************************************/

static Item *MonGetMeasurementStream(EvalContext *ctx, Attributes a, const Promise *pp, PromiseResult *result)
{
 int i;
 
 for (i = 0; i < CF_DUNBAR_WORK; i++)
    {
    if (ENTERPRISE_DATA[i].path == NULL)
       {
       break;
       }
    
    if (strcmp(ENTERPRISE_DATA[i].path, pp->promiser) == 0)
       {
       ENTERPRISE_DATA[i].output = MonReSample(ctx, i, a, pp, result);
       return ENTERPRISE_DATA[i].output;
       }
    }
 
 ENTERPRISE_DATA[i].path = xstrdup(pp->promiser);
 ENTERPRISE_DATA[i].output = MonReSample(ctx, i, a, pp, result);
 return ENTERPRISE_DATA[i].output;
}
Esempio n. 11
0
static void VerifyProcessOp(Item *procdata, Attributes a, Promise *pp)
{
    int matches = 0, do_signals = true, out_of_range, killed = 0, need_to_restart = true;
    Item *killlist = NULL;

    CfDebug("VerifyProcessOp\n");

    matches = FindPidMatches(procdata, &killlist, a, pp);

/* promise based on number of matches */

    if (a.process_count.min_range != CF_NOINT)  /* if a range is specified */
    {
        if ((matches < a.process_count.min_range) || (matches > a.process_count.max_range))
        {
            cfPS(OUTPUT_LEVEL_VERBOSE, CF_CHG, "", pp, a, " !! Process count for \'%s\' was out of promised range (%d found)\n", pp->promiser, matches);
            AddEphemeralClasses(a.process_count.out_of_range_define, pp->ns);
            out_of_range = true;
        }
        else
        {
            AddEphemeralClasses(a.process_count.in_range_define, pp->ns);
            cfPS(OUTPUT_LEVEL_VERBOSE, CF_NOP, "", pp, a, " -> Process promise for %s is kept", pp->promiser);
            out_of_range = false;
        }
    }
    else
    {
        out_of_range = true;
    }

    if (!out_of_range)
    {
        return;
    }

    if (a.transaction.action == cfa_warn)
    {
        do_signals = false;
    }
    else
    {
        do_signals = true;
    }

/* signal/kill promises for existing matches */

    if (do_signals && (matches > 0))
    {
        if (a.process_stop != NULL)
        {
            if (DONTDO)
            {
                cfPS(OUTPUT_LEVEL_ERROR, CF_WARN, "", pp, a,
                     " -- Need to keep process-stop promise for %s, but only a warning is promised", pp->promiser);
            }
            else
            {
                if (IsExecutable(CommandArg0(a.process_stop)))
                {
                    ShellCommandReturnsZero(a.process_stop, false);
                }
                else
                {
                    cfPS(OUTPUT_LEVEL_VERBOSE, CF_FAIL, "", pp, a,
                         "Process promise to stop %s could not be kept because %s the stop operator failed",
                         pp->promiser, a.process_stop);
                    DeleteItemList(killlist);
                    return;
                }
            }
        }

        killed = DoAllSignals(killlist, a, pp);
    }

/* delegated promise to restart killed or non-existent entries */

    need_to_restart = (a.restart_class != NULL) && (killed || (matches == 0));

    DeleteItemList(killlist);

    if (!need_to_restart)
    {
        cfPS(OUTPUT_LEVEL_VERBOSE, CF_NOP, "", pp, a, " -> No restart promised for %s\n", pp->promiser);
        return;
    }
    else
    {
        if (a.transaction.action == cfa_warn)
        {
            cfPS(OUTPUT_LEVEL_ERROR, CF_WARN, "", pp, a,
                 " -- Need to keep restart promise for %s, but only a warning is promised", pp->promiser);
        }
        else
        {
            cfPS(OUTPUT_LEVEL_INFORM, CF_CHG, "", pp, a, " -> Making a one-time restart promise for %s", pp->promiser);
            NewClass(a.restart_class, pp->ns);
        }
    }
}