示例#1
0
int CopyACLs(const char *src, const char *dst)
{
    acl_t acls;
    struct stat statbuf;
    int ret;

    acls = acl_get_file(src, ACL_TYPE_ACCESS);
    if (!acls)
    {
        if (errno == ENOTSUP)
        {
            return true;
        }
        else
        {
            Log(LOG_LEVEL_ERR, "Can't copy ACLs from '%s'. (acl_get_file: %s)", src, GetErrorStr());
            return false;
        }
    }
    ret = acl_set_file(dst, ACL_TYPE_ACCESS, acls);
    acl_free(acls);
    if (ret != 0)
    {
        if (errno == ENOTSUP)
        {
            return true;
        }
        else
        {
            Log(LOG_LEVEL_ERR, "Can't copy ACLs to '%s'. (acl_set_file: %s)", dst, GetErrorStr());
            return false;
        }
    }

    if (stat(src, &statbuf) != 0)
    {
        Log(LOG_LEVEL_ERR, "Can't copy ACLs from '%s'. (stat: %s)", src, GetErrorStr());
        return false;
    }
    if (!S_ISDIR(statbuf.st_mode))
    {
        return true;
    }

    // For directory, copy default ACL too.
    acls = acl_get_file(src, ACL_TYPE_DEFAULT);
    if (!acls)
    {
        Log(LOG_LEVEL_ERR, "Can't copy ACLs from '%s'. (acl_get_file: %s)", src, GetErrorStr());
        return false;
    }
    ret = acl_set_file(dst, ACL_TYPE_DEFAULT, acls);
    acl_free(acls);
    if (ret != 0)
    {
        Log(LOG_LEVEL_ERR, "Can't copy ACLs to '%s'. (acl_set_file: %s)", dst, GetErrorStr());
        return false;
    }

    return true;
}
示例#2
0
int CheckDefaultClearACL(EvalContext *ctx, const char *file_path, Attributes a, const Promise *pp, PromiseResult *result)
{
    acl_t acl_existing;
    acl_t acl_empty;
    acl_entry_t ace_dummy;
    int retv;
    int retval = false;

    acl_existing = NULL;
    acl_empty = NULL;

    if ((acl_existing = acl_get_file(file_path, ACL_TYPE_DEFAULT)) == NULL)
    {
        Log(LOG_LEVEL_ERR, "Unable to read default acl for '%s'. (acl_get_file: %s)", file_path, GetErrorStr());
        return false;
    }

    retv = acl_get_entry(acl_existing, ACL_FIRST_ENTRY, &ace_dummy);

    switch (retv)
    {
    case -1:
        Log(LOG_LEVEL_VERBOSE, "Couldn't retrieve ACE for '%s'. (acl_get_entry: %s)", file_path, GetErrorStr());
        retval = false;
        break;

    case 0:                    // no entries, as desired
        cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Default ACL on '%s' needs no modification.", file_path);
        retval = true;
        break;

    case 1:                    // entries exist, set empty ACL

        if ((acl_empty = acl_init(0)) == NULL)
        {
            Log(LOG_LEVEL_ERR, "Could not reinitialize ACL for '%s'. (acl_init: %s)", file_path, GetErrorStr());
            retval = false;
            break;
        }

        switch (a.transaction.action)
        {
        case cfa_warn:

            cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a, "Default ACL on '%s' needs to be cleared", file_path);
            *result = PromiseResultUpdate(*result, PROMISE_RESULT_WARN);
            break;

        case cfa_fix:

            if (!DONTDO)
            {
                if (acl_set_file(file_path, ACL_TYPE_DEFAULT, acl_empty) != 0)
                {
                    Log(LOG_LEVEL_ERR, "Could not reset ACL for %s", file_path);
                    retval = false;
                    break;
                }
            }

            cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "Default ACL on '%s' successfully cleared", file_path);
            *result = PromiseResultUpdate(*result, PROMISE_RESULT_CHANGE);
            retval = true;

            break;

        default:
            ProgrammingError("CFEngine: internal error: illegal file action");
            retval = false;
        }

        break;

    default:
        retval = false;
    }

    acl_free(acl_empty);
    acl_free(acl_existing);
    return retval;
}
示例#3
0
static int ACLEquals(acl_t first, acl_t second)
{
    acl_entry_t ace_first;
    acl_entry_t ace_second;
    acl_permset_t perms_first;
    acl_permset_t perms_second;
    int first_cnt;
    int second_cnt;
    int more_aces;
    int retv_perms;

    if ((first_cnt = ACECount(first)) == -1)
    {
        Log(LOG_LEVEL_VERBOSE, "Couldn't count ACEs");
        return -1;
    }

    if ((second_cnt = ACECount(second)) == -1)
    {
        Log(LOG_LEVEL_VERBOSE, "Couldn't count ACEs");
        return -1;
    }

    if (first_cnt != second_cnt)
    {
        return 1;
    }

    if (first_cnt == 0)
    {
        return 0;
    }

// check that every ace of first acl exist in second acl

    more_aces = acl_get_entry(first, ACL_FIRST_ENTRY, &ace_first);

    if (more_aces != 1)         // first must contain at least one entry
    {
        Log(LOG_LEVEL_ERR, "Unable to read ACE. (acl_get_entry: %s)", GetErrorStr());
        return -1;
    }

    while (more_aces)
    {
        /* no ace in second match entity-type and id of first */

        if ((ace_second = FindACE(second, ace_first)) == NULL)
        {
            return 1;
        }

        /* permissions must also match */

        if (acl_get_permset(ace_first, &perms_first) != 0)
        {
            Log(LOG_LEVEL_ERR, "Unable to read permissions. (acl_get_permset: %s)", GetErrorStr());
            return -1;
        }

        if (acl_get_permset(ace_second, &perms_second) != 0)
        {
            Log(LOG_LEVEL_ERR, "Unable to read permissions. (acl_get_permset: %s)", GetErrorStr());
            return -1;
        }

        retv_perms = PermsetEquals(perms_first, perms_second);

        if (retv_perms == -1)
        {
            return -1;
        }
        else if (retv_perms == 1)       // permissions differ
        {
            return 1;
        }

        more_aces = acl_get_entry(first, ACL_NEXT_ENTRY, &ace_first);
    }

    return 0;
}
示例#4
0
static void TransformGidsToGroups(StringSet **list)
{
    StringSet *new_list = StringSetNew();
    StringSetIterator i = StringSetIteratorInit(*list);
    const char *data;
    for (data = StringSetIteratorNext(&i); data; data = StringSetIteratorNext(&i))
    {
        if (strlen(data) != strspn(data, "0123456789"))
        {
            // Cannot possibly be a gid.
            StringSetAdd(new_list, xstrdup(data));
            continue;
        }
        // In groups vs gids, groups take precedence. So check if it exists.
        errno = 0;
        struct group *group_info = getgrnam(data);
        if (!group_info)
        {
            switch (errno)
            {
            case 0:
            case ENOENT:
            case EBADF:
            case ESRCH:
            case EWOULDBLOCK:
            case EPERM:
                // POSIX is apparently ambiguous here. All values mean "not found".
                errno = 0;
                group_info = getgrgid(atoi(data));
                if (!group_info)
                {
                    switch (errno)
                    {
                    case 0:
                    case ENOENT:
                    case EBADF:
                    case ESRCH:
                    case EWOULDBLOCK:
                    case EPERM:
                        // POSIX is apparently ambiguous here. All values mean "not found".
                        //
                        // Neither group nor gid is found. This will lead to an error later, but we don't
                        // handle that here.
                        break;
                    default:
                        Log(LOG_LEVEL_ERR, "Error while checking group name '%s'. (getgrgid: '%s')", data, GetErrorStr());
                        StringSetDestroy(new_list);
                        return;
                    }
                }
                else
                {
                    // Replace gid with group name.
                    StringSetAdd(new_list, xstrdup(group_info->gr_name));
                }
                break;
            default:
                Log(LOG_LEVEL_ERR, "Error while checking group name '%s'. (getgrnam: '%s')", data, GetErrorStr());
                StringSetDestroy(new_list);
                return;
            }
        }
        else
        {
            StringSetAdd(new_list, xstrdup(data));
        }
    }
    StringSet *old_list = *list;
    *list = new_list;
    StringSetDestroy(old_list);
}
示例#5
0
static int CheckPosixLinuxACEs(EvalContext *ctx, Rlist *aces, AclMethod method, const char *file_path, acl_type_t acl_type, Attributes a,
                               const Promise *pp, PromiseResult *result)
{
    acl_t acl_existing;
    acl_t acl_new;
    acl_t acl_tmp;
    acl_entry_t ace_parsed;
    acl_entry_t ace_current;
    acl_permset_t perms;
    char *cf_ace;
    int retv;
    int has_mask;
    Rlist *rp;
    char *acl_type_str;

    acl_new = NULL;
    acl_existing = NULL;
    acl_tmp = NULL;
    has_mask = false;

    acl_type_str = acl_type == ACL_TYPE_ACCESS ? "Access" : "Default";

// read existing acl

    if ((acl_existing = acl_get_file(file_path, acl_type)) == NULL)
    {
        Log(LOG_LEVEL_VERBOSE, "No ACL for '%s' could be read. (acl_get_file: %s)", file_path, GetErrorStr());
        return false;
    }

// allocate memory for temp ace (it needs to reside in a temp acl)

    if ((acl_tmp = acl_init(1)) == NULL)
    {
        Log(LOG_LEVEL_ERR, "New ACL could not be allocated (acl_init: %s)", GetErrorStr());
        acl_free((void *) acl_existing);
        return false;
    }

    if (acl_create_entry(&acl_tmp, &ace_parsed) != 0)
    {
        Log(LOG_LEVEL_ERR, "New ACL could not be allocated (acl_create_entry: %s)", GetErrorStr());
        acl_free((void *) acl_existing);
        acl_free((void *) acl_tmp);
        return false;
    }

// copy existing aces if we are appending

    if (method == ACL_METHOD_APPEND)
    {
        if ((acl_new = acl_dup(acl_existing)) == NULL)
        {
            Log(LOG_LEVEL_ERR, "Error copying existing ACL (acl_dup: %s)", GetErrorStr());
            acl_free((void *) acl_existing);
            acl_free((void *) acl_tmp);
            return false;
        }
    }
    else                        // overwrite existing acl
    {
        if ((acl_new = acl_init(5)) == NULL)    // TODO: Always OK with 5 here ?
        {
            Log(LOG_LEVEL_ERR, "New ACL could not be allocated (acl_init: %s)", GetErrorStr());
            acl_free((void *) acl_existing);
            acl_free((void *) acl_tmp);
            return false;
        }
    }

    for (rp = aces; rp != NULL; rp = rp->next)
    {
        cf_ace = RlistScalarValue(rp);

        if (!ParseEntityPosixLinux(&cf_ace, ace_parsed, &has_mask))
        {
            Log(LOG_LEVEL_ERR, "Error parsing entity in 'cf_ace'.");
            acl_free((void *) acl_existing);
            acl_free((void *) acl_tmp);
            acl_free((void *) acl_new);
            return false;
        }

        // check if an ACE with this entity-type and id already exist in the Posix Linux ACL

        ace_current = FindACE(acl_new, ace_parsed);

        // create new entry in ACL if it did not exist

        if (ace_current == NULL)
        {
            if (acl_create_entry(&acl_new, &ace_current) != 0)
            {
                Log(LOG_LEVEL_ERR, "Failed to allocate ace (acl_create_entry: %s)", GetErrorStr());
                acl_free((void *) acl_existing);
                acl_free((void *) acl_tmp);
                acl_free((void *) acl_new);
                return false;
            }

            // copy parsed entity-type and id

            if (acl_copy_entry(ace_current, ace_parsed) != 0)
            {
                Log(LOG_LEVEL_ERR, "Error copying Linux entry in 'cf_ace' (acl_copy_entry: %s)", GetErrorStr());
                acl_free((void *) acl_existing);
                acl_free((void *) acl_tmp);
                acl_free((void *) acl_new);
                return false;
            }

            // clear ace_current's permissions to avoid ace_parsed from last
            // loop iteration to be taken into account when applying mode below
            if ((acl_get_permset(ace_current, &perms) != 0))
            {
                Log(LOG_LEVEL_ERR, "Error obtaining permset for 'ace_current' (acl_get_permset: %s)", GetErrorStr());
                acl_free((void *) acl_existing);
                acl_free((void *) acl_tmp);
                acl_free((void *) acl_new);
                return false;
            }

            if (acl_clear_perms(perms) != 0)
            {
                Log(LOG_LEVEL_ERR, "Error clearing permset for 'ace_current'. (acl_clear_perms: %s)", GetErrorStr());
                acl_free((void *) acl_existing);
                acl_free((void *) acl_tmp);
                acl_free((void *) acl_new);
                return false;
            }
        }

        // mode string should be prefixed with an entry seperator

        if (*cf_ace != ':')
        {
            Log(LOG_LEVEL_ERR, "No separator before mode-string in 'cf_ace'");
            acl_free((void *) acl_existing);
            acl_free((void *) acl_tmp);
            acl_free((void *) acl_new);
            return false;
        }

        cf_ace += 1;

        if (acl_get_permset(ace_current, &perms) != 0)
        {
            Log(LOG_LEVEL_ERR, "Error obtaining permset for 'cf_ace'");
            acl_free((void *) acl_existing);
            acl_free((void *) acl_tmp);
            acl_free((void *) acl_new);
            return false;
        }

        if (!ParseModePosixLinux(cf_ace, perms))
        {
            Log(LOG_LEVEL_ERR, "Error parsing mode-string in 'cf_ace'");
            acl_free((void *) acl_existing);
            acl_free((void *) acl_tmp);
            acl_free((void *) acl_new);
            return false;
        }

        // only allow permissions exist on posix acls, so we do
        // not check what follows next
    }

// if no mask exists, calculate one (or both?): run acl_calc_mask and add one
    if (!has_mask)
    {
        if (acl_calc_mask(&acl_new) != 0)
        {
            Log(LOG_LEVEL_ERR, "Error calculating new acl mask");
            acl_free((void *) acl_existing);
            acl_free((void *) acl_tmp);
            acl_free((void *) acl_new);
            return false;
        }
    }

    if ((retv = ACLEquals(acl_existing, acl_new)) == -1)
    {
        Log(LOG_LEVEL_ERR, "Error while comparing existing and new ACL, unable to repair.");
        acl_free((void *) acl_existing);
        acl_free((void *) acl_tmp);
        acl_free((void *) acl_new);
        return false;
    }

    if (retv == 1)              // existing and new acl differ, update existing
    {

        switch (a.transaction.action)
        {
        case cfa_warn:

            cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a, "%s ACL on file '%s' needs to be updated", acl_type_str, file_path);
            *result = PromiseResultUpdate(*result, PROMISE_RESULT_WARN);
            break;

        case cfa_fix:

            if (!DONTDO)
            {
                if ((retv = acl_set_file(file_path, acl_type, acl_new)) != 0)
                {
                    Log(LOG_LEVEL_ERR, "Error setting new %s ACL on file '%s' (acl_set_file: %s), are required ACEs present ?",
                          acl_type_str, file_path, GetErrorStr());
                    acl_free((void *) acl_existing);
                    acl_free((void *) acl_tmp);
                    acl_free((void *) acl_new);
                    return false;
                }
            }

            cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "%s ACL on '%s' successfully changed.", acl_type_str, file_path);
            *result = PromiseResultUpdate(*result, PROMISE_RESULT_CHANGE);

            break;

        default:
            ProgrammingError("CFEngine: internal error: illegal file action");
        }

    }
    else
    {
        cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "'%s' ACL on '%s' needs no modification.", acl_type_str, file_path);
    }

    acl_free((void *) acl_existing);
    acl_free((void *) acl_new);
    acl_free((void *) acl_tmp);
    return true;
}
示例#6
0
static bool GetPasswordHash(const char *puser, const struct passwd *passwd_info, const char **result)
{
    // Silence warning.
    (void)puser;

#ifdef HAVE_GETSPNAM
    // If the hash is very short, it's probably a stub. Try getting the shadow password instead.
    if (strlen(passwd_info->pw_passwd) <= 4)
    {
        Log(LOG_LEVEL_VERBOSE, "Getting user '%s' password hash from shadow database.", puser);

        struct spwd *spwd_info;
        errno = 0;
        spwd_info = getspnam(puser);
        if (!spwd_info)
        {
            if (errno)
            {
                Log(LOG_LEVEL_ERR, "Could not get information from user shadow database. (getspnam: '%s')", GetErrorStr());
                return false;
            }
            else
            {
                Log(LOG_LEVEL_ERR, "Could not find user when checking password.");
                return false;
            }
        }
        else if (spwd_info)
        {
            *result = spwd_info->sp_pwdp;
            return true;
        }
    }
#endif // HAVE_GETSPNAM
    Log(LOG_LEVEL_VERBOSE, "Getting user '%s' password hash from passwd database.", puser);
    *result = passwd_info->pw_passwd;
    return true;
}
示例#7
0
static bool ChangePasswordHashUsingLckpwdf(const char *puser, const char *password)
{
    bool result = false;

    struct stat statbuf;
    const char *passwd_file = "/etc/shadow";
    if (stat(passwd_file, &statbuf) == -1)
    {
        passwd_file = "/etc/passwd";
    }

    Log(LOG_LEVEL_VERBOSE, "Changing password hash for user '%s' by editing '%s'.", puser, passwd_file);

    if (lckpwdf() != 0)
    {
        Log(LOG_LEVEL_ERR, "Not able to obtain lock on password database.");
        return false;
    }

    char backup_file[strlen(passwd_file) + strlen(".cf-backup") + 1];
    snprintf(backup_file, sizeof(backup_file), "%s.cf-backup", passwd_file);
    unlink(backup_file);

    char edit_file[strlen(passwd_file) + strlen(".cf-edit") + 1];
    snprintf(edit_file, sizeof(edit_file), "%s.cf-edit", passwd_file);
    unlink(edit_file);

    if (!CopyRegularFileDisk(passwd_file, backup_file))
    {
        Log(LOG_LEVEL_ERR, "Could not back up existing password database '%s' to '%s'.", passwd_file, backup_file);
        goto unlock_passwd;
    }

    FILE *passwd_fd = fopen(passwd_file, "r");
    if (!passwd_fd)
    {
        Log(LOG_LEVEL_ERR, "Could not open password database '%s'. (fopen: '%s')", passwd_file, GetErrorStr());
        goto unlock_passwd;
    }
    int edit_fd_int = open(edit_file, O_WRONLY | O_CREAT | O_EXCL, S_IWUSR);
    if (edit_fd_int < 0)
    {
        if (errno == EEXIST)
        {
            Log(LOG_LEVEL_CRIT, "Temporary file already existed when trying to open '%s'. (open: '%s') "
                "This should NEVER happen and could mean that someone is trying to break into your system!!",
                edit_file, GetErrorStr());
        }
        else
        {
            Log(LOG_LEVEL_ERR, "Could not open password database temporary file '%s'. (open: '%s')", edit_file, GetErrorStr());
        }
        goto close_passwd_fd;
    }
    FILE *edit_fd = fdopen(edit_fd_int, "w");
    if (!edit_fd)
    {
        Log(LOG_LEVEL_ERR, "Could not open password database temporary file '%s'. (fopen: '%s')", edit_file, GetErrorStr());
        close(edit_fd_int);
        goto close_passwd_fd;
    }

    while (true)
    {
        size_t line_size = CF_BUFSIZE;
        char *line = xmalloc(line_size);

        int read_result = getline(&line, &line_size, passwd_fd);
        if (read_result < 0)
        {
            if (!feof(passwd_fd))
            {
                Log(LOG_LEVEL_ERR, "Error while reading password database: %s", GetErrorStr());
                free(line);
                goto close_both;
            }
            else
            {
                break;
            }
        }
        else if (read_result >= sizeof(line))
        {
            Log(LOG_LEVEL_ERR, "Unusually long line found in password database while editing user '%s'. Not updating.",
                puser);
        }

        // Editing the password database is risky business, so do as little parsing as possible.
        // Just enough to get the hash in there.
        char *field_start = NULL;
        char *field_end = NULL;
        field_start = strchr(line, ':');
        if (field_start)
        {
            field_end = strchr(field_start + 1, ':');
        }
        if (!field_start || !field_end)
        {
            Log(LOG_LEVEL_ERR, "Unexpected format found in password database while editing user '%s'. Not updating.",
                puser);
            free(line);
            goto close_both;
        }

        // Worst case length: Existing password is empty plus one '\n' and one '\0'.
        char new_line[strlen(line) + strlen(password) + 2];
        *field_start = '\0';
        *field_end = '\0';
        if (strcmp(line, puser) == 0)
        {
            sprintf(new_line, "%s:%s:%s\n", line, password, field_end + 1);
        }
        else
        {
            sprintf(new_line, "%s:%s:%s\n", line, field_start + 1, field_end + 1);
        }

        free(line);

        size_t new_line_size = strlen(new_line);
        size_t written_so_far = 0;
        while (written_so_far < new_line_size)
        {
            clearerr(edit_fd);
            size_t written = fwrite(new_line, 1, new_line_size, edit_fd);
            if (written == 0)
            {
                const char *err_str;
                if (ferror(edit_fd))
                {
                    err_str = GetErrorStr();
                }
                else
                {
                    err_str = "Unknown error";
                }
                Log(LOG_LEVEL_ERR, "Error while writing to file '%s'. (fwrite: '%s')", edit_file, err_str);
                goto close_both;
            }
            written_so_far += written;
        }
    }

    fclose(edit_fd);
    fclose(passwd_fd);

    if (!CopyFilePermissionsDisk(passwd_file, edit_file))
    {
        Log(LOG_LEVEL_ERR, "Could not copy permissions from '%s' to '%s'", passwd_file, edit_file);
        goto unlock_passwd;
    }

    if (rename(edit_file, passwd_file) < 0)
    {
        Log(LOG_LEVEL_ERR, "Could not replace '%s' with edited password database '%s'. (rename: '%s')",
            passwd_file, edit_file, GetErrorStr());
        goto unlock_passwd;
    }

    result = true;

    goto unlock_passwd;

close_both:
    fclose(edit_fd);
    unlink(edit_file);
close_passwd_fd:
    fclose(passwd_fd);
unlock_passwd:
    ulckpwdf();

    return result;
}
int LoadProcessTable(Item **procdata)
{
    FILE *prp;
    char pscomm[CF_MAXLINKSIZE];
    Item *rootprocs = NULL;
    Item *otherprocs = NULL;


    if (PROCESSTABLE)
    {
        Log(LOG_LEVEL_VERBOSE, "Reusing cached process table");
        return true;
    }

    CheckPsLineLimitations();

    const char *psopts = GetProcessOptions();

    snprintf(pscomm, CF_MAXLINKSIZE, "%s %s", VPSCOMM[VPSHARDCLASS], psopts);

    Log(LOG_LEVEL_VERBOSE, "Observe process table with %s", pscomm);

    if ((prp = cf_popen(pscomm, "r", false)) == NULL)
    {
        Log(LOG_LEVEL_ERR, "Couldn't open the process list with command '%s'. (popen: %s)", pscomm, GetErrorStr());
        return false;
    }

    size_t vbuff_size = CF_BUFSIZE;
    char *vbuff = xmalloc(vbuff_size);

# ifdef HAVE_GETZONEID

    char *names[CF_PROCCOLS];
    int start[CF_PROCCOLS];
    int end[CF_PROCCOLS];
    Seq *pidlist = SeqNew(1, NULL);
    Seq *rootpidlist = SeqNew(1, NULL);
    bool global_zone = IsGlobalZone();

    if (global_zone)
    {
        int res = ZLoadProcesstable(pidlist, rootpidlist);

        if (res == false)
        {
            Log(LOG_LEVEL_ERR, "Unable to load solaris zone process table.");
            return false;
        }
    }

# endif

    for (;;)
    {
        ssize_t res = CfReadLine(&vbuff, &vbuff_size, prp);
        if (res == -1)
        {
            if (!feof(prp))
            {
                Log(LOG_LEVEL_ERR, "Unable to read process list with command '%s'. (fread: %s)", pscomm, GetErrorStr());
                cf_pclose(prp);
                free(vbuff);
                return false;
            }
            else
            {
                break;
            }
        }
        Chop(vbuff, vbuff_size);

# ifdef HAVE_GETZONEID

        if (global_zone)
        {
            if (strstr(vbuff, "PID") != NULL)
            {   /* this is the banner so get the column header names for later use*/
                GetProcessColumnNames(vbuff, &names[0], start, end);
            }
            else
            {
               int gpid = ExtractPid(vbuff, names, end);

               if (!IsGlobalProcess(gpid, pidlist, rootpidlist))
               {
                    continue;
               }
            }
        }

# endif
        AppendItem(procdata, vbuff, "");
    }

    cf_pclose(prp);

/* Now save the data */

    snprintf(vbuff, CF_MAXVARSIZE, "%s/state/cf_procs", CFWORKDIR);
    RawSaveItemList(*procdata, vbuff, NewLineMode_Unix);

# ifdef HAVE_GETZONEID
    if (global_zone) /* pidlist and rootpidlist are empty if we're not in the global zone */
    {
        Item *ip = *procdata;
        while (ip != NULL)
        {
            ZCopyProcessList(&rootprocs, ip, rootpidlist, names, end);
            ip = ip->next;
        }
        ReverseItemList(rootprocs);
        ip = *procdata;
        while (ip != NULL)
        {
            ZCopyProcessList(&otherprocs, ip, pidlist, names, end);
            ip = ip->next;
        }
        ReverseItemList(otherprocs);
    }
    else
# endif
    {
        CopyList(&rootprocs, *procdata);
        CopyList(&otherprocs, *procdata);

        while (DeleteItemNotContaining(&rootprocs, "root"))
        {
        }

        while (DeleteItemContaining(&otherprocs, "root"))
        {
        }
    }
    if (otherprocs)
    {
        PrependItem(&rootprocs, otherprocs->name, NULL);
    }
    snprintf(vbuff, CF_MAXVARSIZE, "%s/state/cf_rootprocs", CFWORKDIR);
    RawSaveItemList(rootprocs, vbuff, NewLineMode_Unix);
    DeleteItemList(rootprocs);

    snprintf(vbuff, CF_MAXVARSIZE, "%s/state/cf_otherprocs", CFWORKDIR);
    RawSaveItemList(otherprocs, vbuff, NewLineMode_Unix);
    DeleteItemList(otherprocs);

    free(vbuff);
    return true;
}
示例#9
0
static void TransformGidsToGroups(StringSet **list)
{
    StringSet *new_list = StringSetNew();
    StringSetIterator i = StringSetIteratorInit(*list);
    const char *data;
    for (data = StringSetIteratorNext(&i); data; data = StringSetIteratorNext(&i))
    {
        if (strlen(data) != strspn(data, "0123456789"))
        {
            // Cannot possibly be a gid.
            StringSetAdd(new_list, xstrdup(data));
            continue;
        }
        // In groups vs gids, groups take precedence. So check if it exists.
        struct group *group_info = GetGrEntry(data, &EqualGroupName);
        if (!group_info)
        {
            if (errno == 0)
            {
                group_info = GetGrEntry(data, &EqualGid);
                if (!group_info)
                {
                    if (errno != 0)
                    {
                        Log(LOG_LEVEL_ERR, "Error while checking group name '%s': %s", data, GetErrorStr());
                        StringSetDestroy(new_list);
                        return;
                    }
                    // Neither group nor gid is found. This will lead to an error later, but we don't
                    // handle that here.
                }
                else
                {
                    // Replace gid with group name.
                    StringSetAdd(new_list, xstrdup(group_info->gr_name));
                }
            }
            else
            {
                Log(LOG_LEVEL_ERR, "Error while checking group name '%s': '%s'", data, GetErrorStr());
                StringSetDestroy(new_list);
                return;
            }
        }
        else
        {
            StringSetAdd(new_list, xstrdup(data));
        }
    }
    StringSet *old_list = *list;
    *list = new_list;
    StringSetDestroy(old_list);
}
示例#10
0
static bool MissingInputFile(const char *input_file)
{
    struct stat sb;

    if (stat(input_file, &sb) == -1)
    {
        Log(LOG_LEVEL_ERR, "There is no readable input file at '%s'. (stat: %s)", input_file, GetErrorStr());
        return true;
    }

    return false;
}
/* Load processes using zone-aware ps
 * to obtain solaris list of global
 * process ids for root and non-root
 * users to lookup later */
int ZLoadProcesstable(Seq *pidlist, Seq *rootpidlist)
{

    char *names[CF_PROCCOLS];
    int start[CF_PROCCOLS];
    int end[CF_PROCCOLS];

    int index = 0;
    const char *pscmd = "/usr/bin/ps -Aleo zone,user,pid";

    FILE *psf = cf_popen(pscmd, "r", false);
    if (psf == NULL)
    {
        Log(LOG_LEVEL_ERR, "ZLoadProcesstable: Couldn't open the process list with command %s.", pscmd);
        return false;
    }

    size_t pbuff_size = CF_BUFSIZE;
    char *pbuff = xmalloc(pbuff_size);

    while (true)
    {
        ssize_t res = CfReadLine(&pbuff, &pbuff_size, psf);
        if (res == -1)
        {
            if (!feof(psf))
            {
                Log(LOG_LEVEL_ERR, "IsGlobalProcess(char **, int): Unable to read process list with command '%s'. (fread: %s)", pscmd, GetErrorStr());
                cf_pclose(psf);
                free(pbuff);
                return false;
            }
            else
            {
                break;
            }
        }
        Chop(pbuff, pbuff_size);
        if (strstr(pbuff, "PID")) /* This line is the header. */
        {
            GetProcessColumnNames(pbuff, &names[0], start, end);
        }
        else
        {
            int pid = ExtractPid(pbuff, &names[0], end);

            size_t zone_offset = strspn(pbuff, " ");
            size_t zone_end_offset = strcspn(pbuff + zone_offset, " ") + zone_offset;
            size_t user_offset = strspn(pbuff + zone_end_offset, " ") + zone_end_offset;
            size_t user_end_offset = strcspn(pbuff + user_offset, " ") + user_offset;
            bool is_global = (zone_end_offset - zone_offset == 6
                                  && strncmp(pbuff + zone_offset, "global", 6) == 0);
            bool is_root = (user_end_offset - user_offset == 4
                                && strncmp(pbuff + user_offset, "root", 4) == 0);

            if (is_global && is_root)
            {
                SeqAppend(rootpidlist, (void*)(intptr_t)pid);
            }
            else if (is_global && !is_root)
            {
                SeqAppend(pidlist, (void*)(intptr_t)pid);
            }
        }
    }
    cf_pclose(psf);
    free(pbuff);
    return true;
}
示例#12
0
void GenericAgentInitialize(EvalContext *ctx, GenericAgentConfig *config)
{
    int force = false;
    struct stat statbuf, sb;
    char vbuff[CF_BUFSIZE];
    char ebuff[CF_EXPANDSIZE];

#ifdef __MINGW32__
    InitializeWindows();
#endif

    DetermineCfenginePort();

    EvalContextClassPutHard(ctx, "any", "source=agent");

    GenericAgentAddEditionClasses(ctx);

/* Define trusted directories */

    {
        const char *workdir = GetWorkDir();
        if (!workdir)
        {
            FatalError(ctx, "Error determining working directory");
        }

        strcpy(CFWORKDIR, workdir);
        MapName(CFWORKDIR);
    }

    OpenLog(LOG_USER);
    SetSyslogFacility(LOG_USER);

    Log(LOG_LEVEL_VERBOSE, "Work directory is %s", CFWORKDIR);

    snprintf(vbuff, CF_BUFSIZE, "%s%cupdate.conf", GetInputDir(), FILE_SEPARATOR);
    MakeParentDirectory(vbuff, force);
    snprintf(vbuff, CF_BUFSIZE, "%s%cbin%ccf-agent -D from_cfexecd", CFWORKDIR, FILE_SEPARATOR, FILE_SEPARATOR);
    MakeParentDirectory(vbuff, force);
    snprintf(vbuff, CF_BUFSIZE, "%s%coutputs%cspooled_reports", CFWORKDIR, FILE_SEPARATOR, FILE_SEPARATOR);
    MakeParentDirectory(vbuff, force);
    snprintf(vbuff, CF_BUFSIZE, "%s%clastseen%cintermittencies", CFWORKDIR, FILE_SEPARATOR, FILE_SEPARATOR);
    MakeParentDirectory(vbuff, force);
    snprintf(vbuff, CF_BUFSIZE, "%s%creports%cvarious", CFWORKDIR, FILE_SEPARATOR, FILE_SEPARATOR);
    MakeParentDirectory(vbuff, force);

    snprintf(vbuff, CF_BUFSIZE, "%s", GetInputDir());

    if (stat(vbuff, &sb) == -1)
    {
        FatalError(ctx, " No access to WORKSPACE/inputs dir");
    }
    else
    {
        chmod(vbuff, sb.st_mode | 0700);
    }

    snprintf(vbuff, CF_BUFSIZE, "%s%coutputs", CFWORKDIR, FILE_SEPARATOR);

    if (stat(vbuff, &sb) == -1)
    {
        FatalError(ctx, " No access to WORKSPACE/outputs dir");
    }
    else
    {
        chmod(vbuff, sb.st_mode | 0700);
    }

    snprintf(ebuff, sizeof(ebuff), "%s%cstate%ccf_procs",
             CFWORKDIR, FILE_SEPARATOR, FILE_SEPARATOR);
    MakeParentDirectory(ebuff, force);

    if (stat(ebuff, &statbuf) == -1)
    {
        CreateEmptyFile(ebuff);
    }

    snprintf(ebuff, sizeof(ebuff), "%s%cstate%ccf_rootprocs",
             CFWORKDIR, FILE_SEPARATOR, FILE_SEPARATOR);

    if (stat(ebuff, &statbuf) == -1)
    {
        CreateEmptyFile(ebuff);
    }

    snprintf(ebuff, sizeof(ebuff), "%s%cstate%ccf_otherprocs",
             CFWORKDIR, FILE_SEPARATOR, FILE_SEPARATOR);

    if (stat(ebuff, &statbuf) == -1)
    {
        CreateEmptyFile(ebuff);
    }

    snprintf(ebuff, sizeof(ebuff), "%s%cstate%cprevious_state%c",
             CFWORKDIR, FILE_SEPARATOR, FILE_SEPARATOR, FILE_SEPARATOR);
    MakeParentDirectory(ebuff, force);

    snprintf(ebuff, sizeof(ebuff), "%s%cstate%cdiff%c",
             CFWORKDIR, FILE_SEPARATOR, FILE_SEPARATOR, FILE_SEPARATOR);
    MakeParentDirectory(ebuff, force);

    snprintf(ebuff, sizeof(ebuff), "%s%cstate%cuntracked%c",
            CFWORKDIR, FILE_SEPARATOR, FILE_SEPARATOR, FILE_SEPARATOR);
    MakeParentDirectory(ebuff, force);

    OpenNetwork();
    CryptoInitialize();

    CheckWorkingDirectories(ctx);

    /* Initialize keys and networking. cf-key, doesn't need keys. In fact it
       must function properly even without them, so that it generates them! */
    if (config->agent_type != AGENT_TYPE_KEYGEN)
    {
        LoadSecretKeys();
        char *bootstrapped_policy_server = ReadPolicyServerFile(CFWORKDIR);
        PolicyHubUpdateKeys(bootstrapped_policy_server);
        free(bootstrapped_policy_server);
        cfnet_init();
    }

    size_t cwd_size = PATH_MAX;
    while (true)
    {
        char cwd[cwd_size];
        if (!getcwd(cwd, cwd_size))
        {
            if (errno == ERANGE)
            {
                cwd_size *= 2;
                continue;
            }
            Log(LOG_LEVEL_WARNING, "Could not determine current directory. (getcwd: '%s')", GetErrorStr());
            break;
        }
        EvalContextSetLaunchDirectory(ctx, cwd);
        break;
    }

    if (!MINUSF)
    {
        GenericAgentConfigSetInputFile(config, GetInputDir(), "promises.cf");
    }

    VIFELAPSED = 1;
    VEXPIREAFTER = 1;

    setlinebuf(stdout);

    if (config->agent_specific.agent.bootstrap_policy_server)
    {
        snprintf(vbuff, CF_BUFSIZE, "%s%cfailsafe.cf", GetInputDir(), FILE_SEPARATOR);

        if (stat(vbuff, &statbuf) == -1)
        {
            GenericAgentConfigSetInputFile(config, GetInputDir(), "failsafe.cf");
        }
        else
        {
            GenericAgentConfigSetInputFile(config, GetInputDir(), vbuff);
        }
    }
}
示例#13
0
/**
 * @brief Writes a file with a contained release ID based on git SHA,
 *        or file checksum if git SHA is not available.
 * @param filename the release_id file
 * @param dirname the directory to checksum or get the Git hash
 * @return True if successful
 */
static bool WriteReleaseIdFile(const char *filename, const char *dirname)
{
    char release_id[GENERIC_AGENT_CHECKSUM_SIZE];

    bool have_release_id =
        GeneratePolicyReleaseID(release_id, sizeof(release_id), dirname);

    if (!have_release_id)
    {
        return false;
    }

    int fd = creat(filename, 0600);
    if (fd == -1)
    {
        Log(LOG_LEVEL_ERR, "While writing policy release ID file '%s', could not create file (creat: %s)", filename, GetErrorStr());
        return false;
    }

    JsonElement *info = JsonObjectCreate(3);
    JsonObjectAppendString(info, "releaseId", release_id);

    Writer *w = FileWriter(fdopen(fd, "w"));
    JsonWrite(w, info, 0);

    WriterClose(w);
    JsonDestroy(info);

    Log(LOG_LEVEL_VERBOSE, "Saved policy release ID file '%s'", filename);
    return true;
}
示例#14
0
/**
 * @brief Writes a file with a contained timestamp to mark a policy file as validated
 * @param filename the filename
 * @return True if successful.
 */
static bool WritePolicyValidatedFile(ARG_UNUSED const GenericAgentConfig *config, const char *filename)
{
    if (!MakeParentDirectory(filename, true))
    {
        Log(LOG_LEVEL_ERR, "While writing policy validated marker file '%s', could not create directory (MakeParentDirectory: %s)", filename, GetErrorStr());
        return false;
    }

    int fd = creat(filename, 0600);
    if (fd == -1)
    {
        Log(LOG_LEVEL_ERR, "While writing policy validated marker file '%s', could not create file (creat: %s)", filename, GetErrorStr());
        return false;
    }

    JsonElement *info = JsonObjectCreate(3);
    JsonObjectAppendInteger(info, "timestamp", time(NULL));

    Writer *w = FileWriter(fdopen(fd, "w"));
    JsonWrite(w, info, 0);

    WriterClose(w);
    JsonDestroy(info);

    Log(LOG_LEVEL_VERBOSE, "Saved policy validated marker file '%s'", filename);
    return true;
}
示例#15
0
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;
}
示例#16
0
bool CopyFileExtendedAttributesDisk(const char *source, const char *destination)
{
#if defined(WITH_XATTR)
    // Extended attributes include both POSIX ACLs and SELinux contexts.
    ssize_t attr_raw_names_size;
    char attr_raw_names[CF_BUFSIZE];

    attr_raw_names_size = llistxattr(source, attr_raw_names, sizeof(attr_raw_names));
    if (attr_raw_names_size < 0)
    {
        if (errno == ENOTSUP || errno == ENODATA)
        {
            return true;
        }
        else
        {
            Log(LOG_LEVEL_ERR, "Can't copy extended attributes from '%s' to '%s'. (llistxattr: %s)",
                source, destination, GetErrorStr());
            return false;
        }
    }

    int pos;
    for (pos = 0; pos < attr_raw_names_size;)
    {
        const char *current = attr_raw_names + pos;
        pos += strlen(current) + 1;

        char data[CF_BUFSIZE];
        int datasize = lgetxattr(source, current, data, sizeof(data));
        if (datasize < 0)
        {
            if (errno == ENOTSUP)
            {
                continue;
            }
            else
            {
                Log(LOG_LEVEL_ERR, "Can't copy extended attributes from '%s' to '%s'. (lgetxattr: %s: %s)",
                    source, destination, GetErrorStr(), current);
                return false;
            }
        }

        int ret = lsetxattr(destination, current, data, datasize, 0);
        if (ret < 0)
        {
            if (errno == ENOTSUP)
            {
                continue;
            }
            else
            {
                Log(LOG_LEVEL_ERR, "Can't copy extended attributes from '%s' to '%s'. (lsetxattr: %s: %s)",
                    source, destination, GetErrorStr(), current);
                return false;
            }
        }
    }

#else // !WITH_XATTR
    // ACLs are included in extended attributes, but fall back to CopyACLs if xattr is not available.
    if (!CopyACLs(source, destination))
    {
        return false;
    }
#endif

    return true;
}
示例#17
0
int ExpandLinks(char *dest, const char *from, int level)
{
    char buff[CF_BUFSIZE];
    char node[CF_MAXLINKSIZE];
    struct stat statbuf;
    int lastnode = false;

    memset(dest, 0, CF_BUFSIZE);

    if (level >= CF_MAXLINKLEVEL)
    {
        Log(LOG_LEVEL_ERR, "Too many levels of symbolic links to evaluate absolute path");
        return false;
    }

    const char *sp = from;

    while (*sp != '\0')
    {
        if (*sp == FILE_SEPARATOR)
        {
            sp++;
            continue;
        }

        sscanf(sp, "%[^/]", node);
        sp += strlen(node);

        if (*sp == '\0')
        {
            lastnode = true;
        }

        if (strcmp(node, ".") == 0)
        {
            continue;
        }

        if (strcmp(node, "..") == 0)
        {
            continue;
        }
        else
        {
            strcat(dest, "/");
        }

        strcat(dest, node);

        if (lstat(dest, &statbuf) == -1)        /* File doesn't exist so we can stop here */
        {
            Log(LOG_LEVEL_ERR, "Can't stat '%s' in ExpandLinks. (lstat: %s)", dest, GetErrorStr());
            return false;
        }

        if (S_ISLNK(statbuf.st_mode))
        {
            memset(buff, 0, CF_BUFSIZE);

            if (readlink(dest, buff, CF_BUFSIZE - 1) == -1)
            {
                Log(LOG_LEVEL_ERR, "Expand links can't stat '%s'. (readlink: %s)", dest, GetErrorStr());
                return false;
            }
            else
            {
                if (buff[0] == '.')
                {
                    ChopLastNode(dest);
                    AddSlash(dest);

                    /* TODO pass and use parameter dest_size. */
                    size_t ret = strlcat(dest, buff, CF_BUFSIZE);
                    if (ret >= CF_BUFSIZE)
                    {
                        Log(LOG_LEVEL_ERR,
                            "Internal limit reached in ExpandLinks(),"
                            " path too long: '%s' + '%s'",
                            dest, buff);
                        return false;
                    }
                }
                else if (IsAbsoluteFileName(buff))
                {
                    strcpy(dest, buff);
                    DeleteSlash(dest);

                    if (strcmp(dest, from) == 0)
                    {
                        Log(LOG_LEVEL_DEBUG, "No links to be expanded");
                        return true;
                    }

                    if ((!lastnode) && (!ExpandLinks(buff, dest, level + 1)))
                    {
                        return false;
                    }
                }
                else
                {
                    ChopLastNode(dest);
                    AddSlash(dest);

                    /* TODO use param dest_size. */
                    size_t ret = strlcat(dest, buff, CF_BUFSIZE);
                    if (ret >= CF_BUFSIZE)
                    {
                        Log(LOG_LEVEL_ERR,
                            "Internal limit reached in ExpandLinks end,"
                            " path too long: '%s' + '%s'", dest, buff);
                        return false;
                    }

                    DeleteSlash(dest);

                    if (strcmp(dest, from) == 0)
                    {
                        Log(LOG_LEVEL_DEBUG, "No links to be expanded");
                        return true;
                    }

                    memset(buff, 0, CF_BUFSIZE);

                    if ((!lastnode) && (!ExpandLinks(buff, dest, level + 1)))
                    {
                        return false;
                    }
                }
            }
        }
    }

    return true;
}
示例#18
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 (a.contain.shelltype == SHELL_TYPE_NONE)
    {
        if (!IsExecutable(CommandArg0(pp->promiser)))
        {
            Log(LOG_LEVEL_ERR, "'%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;
}
示例#19
0
void VerifyOneUsersPromise (const char *puser, User u, PromiseResult *result, enum cfopaction action,
                            EvalContext *ctx, const Attributes *a, const Promise *pp)
{
    bool res;

    struct passwd *passwd_info;
    errno = 0;
    passwd_info = getpwnam(puser);
    // Apparently POSIX is ambiguous here. All the values below mean "not found".
    if (!passwd_info && errno != 0 && errno != ENOENT && errno != EBADF && errno != ESRCH
            && errno != EWOULDBLOCK && errno != EPERM)
    {
        Log(LOG_LEVEL_ERR, "Could not get information from user database. (getpwnam: '%s')", GetErrorStr());
        return;
    }

    if (u.policy == USER_STATE_PRESENT || u.policy == USER_STATE_LOCKED)
    {
        if (passwd_info)
        {
            uint32_t cmap = 0;
            if (VerifyIfUserNeedsModifs (puser, u, passwd_info, &cmap))
            {
                res = DoModifyUser (puser, u, passwd_info, cmap, action);
                if (res)
                {
                    *result = PROMISE_RESULT_CHANGE;
                }
                else
                {
                    *result = PROMISE_RESULT_FAIL;
                }
            }
            else
            {
                *result = PROMISE_RESULT_NOOP;
            }
        }
        else
        {
            res = DoCreateUser (puser, u, action, ctx, a, pp);
            if (res)
            {
                *result = PROMISE_RESULT_CHANGE;
            }
            else
            {
                *result = PROMISE_RESULT_FAIL;
            }
        }
    }
    else if (u.policy == USER_STATE_ABSENT)
    {
        if (passwd_info)
        {
            res = DoRemoveUser (puser, action);
            if (res)
            {
                *result = PROMISE_RESULT_CHANGE;
            }
            else
            {
                *result = PROMISE_RESULT_FAIL;
            }
        }
        else
        {
            *result = PROMISE_RESULT_NOOP;
        }
    }
}
示例#20
0
void StartServer(EvalContext *ctx, Policy **policy, GenericAgentConfig *config)
{
    int sd = -1, sd_reply;
    fd_set rset;
    struct timeval timeout;
    int ret_val;
    CfLock thislock;
    time_t starttime = time(NULL), last_collect = 0;

    struct sockaddr_storage cin;
    socklen_t addrlen = sizeof(cin);

    signal(SIGINT, HandleSignalsForDaemon);
    signal(SIGTERM, HandleSignalsForDaemon);
    signal(SIGHUP, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGUSR1, HandleSignalsForDaemon);
    signal(SIGUSR2, HandleSignalsForDaemon);

    sd = SetServerListenState(ctx, QUEUESIZE);

    TransactionContext tc = {
        .ifelapsed = 0,
        .expireafter = 1,
    };

    Policy *server_cfengine_policy = PolicyNew();
    Promise *pp = NULL;
    {
        Bundle *bp = PolicyAppendBundle(server_cfengine_policy, NamespaceDefault(), "server_cfengine_bundle", "agent", NULL, NULL);
        PromiseType *tp = BundleAppendPromiseType(bp, "server_cfengine");

        pp = PromiseTypeAppendPromise(tp, config->input_file, (Rval) { NULL, RVAL_TYPE_NOPROMISEE }, NULL);
    }
    assert(pp);

    thislock = AcquireLock(ctx, pp->promiser, VUQNAME, CFSTARTTIME, tc, pp, false);

    if (thislock.lock == NULL)
    {
        PolicyDestroy(server_cfengine_policy);
        return;
    }

    Log(LOG_LEVEL_INFO, "cf-serverd starting %.24s", ctime(&starttime));

    if (sd != -1)
    {
        Log(LOG_LEVEL_VERBOSE, "Listening for connections ...");
    }

#ifdef __MINGW32__

    if (!NO_FORK)
    {
        Log(LOG_LEVEL_VERBOSE, "Windows does not support starting processes in the background - starting in foreground");
    }

#else /* !__MINGW32__ */

    if ((!NO_FORK) && (fork() != 0))
    {
        _exit(0);
    }

    if (!NO_FORK)
    {
        ActAsDaemon(sd);
    }

#endif /* !__MINGW32__ */

    WritePID("cf-serverd.pid");

/* Andrew Stribblehill <*****@*****.**> -- close sd on exec */
#ifndef __MINGW32__
    fcntl(sd, F_SETFD, FD_CLOEXEC);
#endif

    while (!IsPendingTermination())
    {
        time_t now = time(NULL);

        /* Note that this loop logic is single threaded, but ACTIVE_THREADS
           might still change in threads pertaining to service handling */

        if (ThreadLock(cft_server_children))
        {
            if (ACTIVE_THREADS == 0)
            {
                CheckFileChanges(ctx, policy, config);
            }
            ThreadUnlock(cft_server_children);
        }

        // Check whether we should try to establish peering with a hub

        if ((COLLECT_INTERVAL > 0) && ((now - last_collect) > COLLECT_INTERVAL))
        {
            TryCollectCall();
            last_collect = now;
            continue;
        }

        /* check if listening is working */
        if (sd != -1)
        {
            // Look for normal incoming service requests

            FD_ZERO(&rset);
            FD_SET(sd, &rset);

            /* Set 1 second timeout for select, so that signals are handled in
             * a timely manner */
            timeout.tv_sec = 1;
            timeout.tv_usec = 0;

            Log(LOG_LEVEL_DEBUG, "Waiting at incoming select...");

            ret_val = select((sd + 1), &rset, NULL, NULL, &timeout);

            if (ret_val == -1)      /* Error received from call to select */
            {
                if (errno == EINTR)
                {
                    continue;
                }
                else
                {
                    Log(LOG_LEVEL_ERR, "select failed. (select: %s)", GetErrorStr());
                    exit(1);
                }
            }
            else if (!ret_val) /* No data waiting, we must have timed out! */
            {
                continue;
            }

            Log(LOG_LEVEL_VERBOSE, "Accepting a connection");

            if ((sd_reply = accept(sd, (struct sockaddr *) &cin, &addrlen)) != -1)
            {
                /* Just convert IP address to string, no DNS lookup. */
                char ipaddr[CF_MAX_IP_LEN] = "";
                getnameinfo((struct sockaddr *) &cin, addrlen,
                            ipaddr, sizeof(ipaddr),
                            NULL, 0, NI_NUMERICHOST);

                ServerEntryPoint(ctx, sd_reply, ipaddr);
            }
        }
    }

    PolicyDestroy(server_cfengine_policy);
}

/*********************************************************************/
/* Level 2                                                           */
/*********************************************************************/

int InitServer(size_t queue_size)
{
    int sd = -1;

    if ((sd = OpenReceiverChannel()) == -1)
    {
        Log(LOG_LEVEL_ERR, "Unable to start server");
        exit(1);
    }

    if (listen(sd, queue_size) == -1)
    {
        Log(LOG_LEVEL_ERR, "listen failed. (listen: %s)", GetErrorStr());
        exit(1);
    }

    return sd;
}
示例#21
0
static bool GroupGetUserMembership (const char *user, StringSet *result)
{
    bool ret = true;
    struct group *group_info;

    setgrent();
    while (true)
    {
        errno = 0;
        group_info = getgrent();
        if (!group_info)
        {
            if (errno)
            {
                Log(LOG_LEVEL_ERR, "Error while getting group list. (getgrent: '%s')", GetErrorStr());
                ret = false;
            }
            break;
        }
        for (int i = 0; group_info->gr_mem[i] != NULL; i++)
        {
            if (strcmp(user, group_info->gr_mem[i]) == 0)
            {
                StringSetAdd(result, xstrdup(group_info->gr_name));
                break;
            }
        }
    }
    endgrent();

    return ret;
}
示例#22
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");
    }
}
示例#23
0
static bool VerifyIfUserNeedsModifs (const char *puser, User u, const struct passwd *passwd_info,
                                     uint32_t *changemap)
{
    if (u.description != NULL && strcmp (u.description, passwd_info->pw_gecos))
    {
        CFUSR_SETBIT (*changemap, i_comment);
    }
    if (u.uid != NULL && (atoi (u.uid) != passwd_info->pw_uid))
    {
        CFUSR_SETBIT (*changemap, i_uid);
    }
    if (u.home_dir != NULL && strcmp (u.home_dir, passwd_info->pw_dir))
    {
        CFUSR_SETBIT (*changemap, i_home);
    }
    if (u.shell != NULL && strcmp (u.shell, passwd_info->pw_shell))
    {
        CFUSR_SETBIT (*changemap, i_shell);
    }
    bool account_is_locked = IsAccountLocked(puser, passwd_info);
    if ((!account_is_locked && u.policy == USER_STATE_LOCKED)
            || (account_is_locked && u.policy != USER_STATE_LOCKED))
    {
        CFUSR_SETBIT(*changemap, i_locked);
    }
    // Don't bother with passwords if the account is going to be locked anyway.
    if (u.password != NULL && strcmp (u.password, "")
            && u.policy != USER_STATE_LOCKED)
    {
        if (!IsPasswordCorrect(puser, u.password, u.password_format, passwd_info))
        {
            CFUSR_SETBIT (*changemap, i_password);
        }
    }

    if (SafeStringLength(u.group_primary))
    {
        bool group_could_be_gid = (strlen(u.group_primary) == strspn(u.group_primary, "0123456789"));
        int gid;

        // We try name first, even if it looks like a gid. Only fall back to gid.
        struct group *group_info;
        errno = 0;
        group_info = getgrnam(u.group_primary);
        // Apparently POSIX is ambiguous here. All the values below mean "not found".
        if (!group_info && errno != 0 && errno != ENOENT && errno != EBADF && errno != ESRCH
                && errno != EWOULDBLOCK && errno != EPERM)
        {
            Log(LOG_LEVEL_ERR, "Could not obtain information about group '%s'. (getgrnam: '%s')", u.group_primary, GetErrorStr());
            gid = -1;
        }
        else if (!group_info)
        {
            if (group_could_be_gid)
            {
                gid = atoi(u.group_primary);
            }
            else
            {
                Log(LOG_LEVEL_ERR, "No such group '%s'.", u.group_primary);
                gid = -1;
            }
        }
        else
        {
            gid = group_info->gr_gid;
        }

        if (gid != passwd_info->pw_gid)
        {
            CFUSR_SETBIT (*changemap, i_group);
        }
    }
    if (u.groups_secondary != NULL)
    {
        StringSet *wanted_groups = StringSetNew();
        for (Rlist *ptr = u.groups_secondary; ptr; ptr = ptr->next)
        {
            if (strcmp(RvalScalarValue(ptr->val), CF_NULL_VALUE) != 0)
            {
                StringSetAdd(wanted_groups, xstrdup(RvalScalarValue(ptr->val)));
            }
        }
        TransformGidsToGroups(&wanted_groups);
        StringSet *current_groups = StringSetNew();
        if (!GroupGetUserMembership (puser, current_groups))
        {
            CFUSR_SETBIT (*changemap, i_groups);
        }
        else if (!StringSetIsEqual (current_groups, wanted_groups))
        {
            CFUSR_SETBIT (*changemap, i_groups);
        }
        StringSetDestroy(current_groups);
        StringSetDestroy(wanted_groups);
    }

    ////////////////////////////////////////////
    if (*changemap == 0)
    {
        return false;
    }
    else
    {
        return true;
    }
}
示例#24
0
int IsNewerFileTree(char *dir, time_t reftime)
{
    const struct dirent *dirp;
    char path[CF_BUFSIZE] = { 0 };
    Dir *dirh;
    struct stat sb;

// Assumes that race conditions on the file path are unlikely and unimportant

    if (lstat(dir, &sb) == -1)
    {
        Log(LOG_LEVEL_ERR, "Unable to stat directory '%s' in IsNewerFileTree. (stat: %s)", dir, GetErrorStr());
        // return true to provoke update
        return true;
    }

    if (S_ISDIR(sb.st_mode))
    {
        if (sb.st_mtime > reftime)
        {
            Log(LOG_LEVEL_VERBOSE, " >> Detected change in %s", dir);
            return true;
        }
    }

    if ((dirh = DirOpen(dir)) == NULL)
    {
        Log(LOG_LEVEL_ERR, "Unable to open directory '%s' in IsNewerFileTree. (opendir: %s)", dir, GetErrorStr());
        return false;
    }
    else
    {
        for (dirp = DirRead(dirh); dirp != NULL; dirp = DirRead(dirh))
        {
            if (!strcmp(dirp->d_name, ".") || !strcmp(dirp->d_name, ".."))
            {
                continue;
            }

            strncpy(path, dir, CF_BUFSIZE - 1);

            if (!JoinPath(path, dirp->d_name))
            {
                Log(LOG_LEVEL_ERR, "Internal limit: Buffer ran out of space adding %s to %s in IsNewerFileTree", dir,
                      path);
                DirClose(dirh);
                return false;
            }

            if (lstat(path, &sb) == -1)
            {
                Log(LOG_LEVEL_ERR, "Unable to stat directory '%s' in IsNewerFileTree. (lstat: %s)", path, GetErrorStr());
                DirClose(dirh);
                // return true to provoke update
                return true;
            }

            if (S_ISDIR(sb.st_mode))
            {
                if (sb.st_mtime > reftime)
                {
                    Log(LOG_LEVEL_VERBOSE, " >> Detected change in %s", path);
                    DirClose(dirh);
                    return true;
                }
                else
                {
                    if (IsNewerFileTree(path, reftime))
                    {
                        DirClose(dirh);
                        return true;
                    }
                }
            }
        }
    }

    DirClose(dirh);
    return false;
}
示例#25
0
static int CheckDefaultEqualsAccessACL(EvalContext *ctx, const char *file_path, Attributes a, const Promise *pp, PromiseResult *result)
{
    acl_t acl_access;
    acl_t acl_default;
    int equals;
    int retval = false;

    acl_access = NULL;
    acl_default = NULL;

    if ((acl_access = acl_get_file(file_path, ACL_TYPE_ACCESS)) == NULL)
    {
        Log(LOG_LEVEL_ERR, "Could not find an ACL for '%s'. (acl_get_file: %s)", file_path, GetErrorStr());
        return false;
    }

    acl_default = acl_get_file(file_path, ACL_TYPE_DEFAULT);

    if (acl_default == NULL)
    {
        Log(LOG_LEVEL_ERR, "Could not find default ACL for '%s'. (acl_get_file: %s)", file_path, GetErrorStr());
        acl_free(acl_access);
        return false;
    }

    equals = ACLEquals(acl_access, acl_default);

    switch (equals)
    {
    case 0:                    // they equal, as desired
        cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Default ACL on '%s' needs no modification.", file_path);
        retval = true;
        break;

    case 1:                    // set access ACL as default ACL

        switch (a.transaction.action)
        {
        case cfa_warn:

            cfPS(ctx, LOG_LEVEL_WARNING, PROMISE_RESULT_WARN, pp, a, "Default ACL on '%s' needs to be copied from access ACL.",
                 file_path);
            *result = PromiseResultUpdate(*result, PROMISE_RESULT_WARN);
            break;

        case cfa_fix:

            if (!DONTDO)
            {
                if ((acl_set_file(file_path, ACL_TYPE_DEFAULT, acl_access)) != 0)
                {
                    Log(LOG_LEVEL_ERR, "Could not set default ACL to access");
                    acl_free(acl_access);
                    acl_free(acl_default);
                    return false;
                }
            }

            cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "Default ACL on '%s' successfully copied from access ACL.",
                 file_path);
            *result = PromiseResultUpdate(*result, PROMISE_RESULT_CHANGE);
            retval = true;

            break;

        default:
            ProgrammingError("CFEngine: internal error: illegal file action");
            retval = false;
        }

        break;

    default:
        retval = false;
        Log(LOG_LEVEL_ERR, "Unable to compare access and default ACEs");
    }

    acl_free(acl_access);
    acl_free(acl_default);
    return retval;
}
示例#26
0
ENTERPRISE_VOID_FUNC_2ARG_DEFINE_STUB(void, LogTotalCompliance, const char *, version, int, background_tasks)
{
    double total = (double) (PR_KEPT + PR_NOTKEPT + PR_REPAIRED) / 100.0;

    char string[CF_BUFSIZE] = { 0 };

    snprintf(string, CF_BUFSIZE,
             "Outcome of version %s (" CF_AGENTC "-%d): Promises observed to be kept %.2f%%, Promises repaired %.2f%%, Promises not repaired %.2f%%",
             version, background_tasks,
             (double) PR_KEPT / total,
             (double) PR_REPAIRED / total,
             (double) PR_NOTKEPT / total);

    Log(LOG_LEVEL_VERBOSE, "Logging total compliance, total '%s'", string);

    char filename[CF_BUFSIZE];
    snprintf(filename, CF_BUFSIZE, "%s/%s", GetLogDir(), CF_PROMISE_LOG);
    MapName(filename);

    FILE *fout = fopen(filename, "a");
    if (fout == NULL)
    {
        Log(LOG_LEVEL_ERR, "In total compliance logging, could not open file '%s'. (fopen: %s)", filename, GetErrorStr());
    }
    else
    {
        fprintf(fout, "%jd,%jd: %s\n", (intmax_t)CFSTARTTIME, (intmax_t)time(NULL), string);
        fclose(fout);
    }
}
示例#27
0
static acl_entry_t FindACE(acl_t acl, acl_entry_t ace_find)
{
    acl_entry_t ace_curr;
    acl_tag_t tag_curr;
    acl_tag_t tag_find;
    id_t *id_curr;
    id_t *id_find;
    int more_aces;
    int retv_tag;

    id_find = NULL;

    more_aces = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace_curr);

    if (more_aces == -1)
    {
        Log(LOG_LEVEL_ERR, "Error reading acl. (acl_get_entry: %s)", GetErrorStr());
        return NULL;
    }
    else if (more_aces == 0)
    {
        return NULL;
    }

/* find the tag type and id we are looking for */

    if (acl_get_tag_type(ace_find, &tag_find) != 0)
    {
        Log(LOG_LEVEL_ERR, "Error reading tag type. (acl_get_tag_type: %s)", GetErrorStr());
        return NULL;
    }

    if (tag_find == ACL_USER || tag_find == ACL_GROUP)
    {
        id_find = acl_get_qualifier(ace_find);

        if (id_find == NULL)
        {
            Log(LOG_LEVEL_ERR, "Error reading tag type. (acl_get_qualifier: %s)", GetErrorStr());
            return NULL;
        }
    }

/* check if any of the aces match */

    while (more_aces)
    {
        if ((retv_tag = acl_get_tag_type(ace_curr, &tag_curr)) != 0)
        {
            Log(LOG_LEVEL_ERR, "Unable to get tag type. (acl_get_tag_type: %s)", GetErrorStr());
            acl_free(id_find);
            return NULL;
        }

        if (tag_curr == tag_find)
        {
            if (id_find == NULL)
            {
                return ace_curr;
            }

            id_curr = acl_get_qualifier(ace_curr);

            if (id_curr == NULL)
            {
                Log(LOG_LEVEL_ERR, "Couldn't extract qualifier. (acl_get_qualifier: %s)", GetErrorStr());
                return NULL;
            }

            if (*id_curr == *id_find)
            {
                acl_free(id_find);
                acl_free(id_curr);
                return ace_curr;
            }

            acl_free(id_curr);
        }

        more_aces = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace_curr);
    }

    if (id_find != NULL)
    {
        acl_free(id_find);
    }

    return NULL;
}
示例#28
0
void RemoteSysLog(int log_priority, const char *log_string)
{
    time_t now = time(NULL);

    struct addrinfo query = { 0 }, *response = NULL;
    char strport[PRINTSIZE(unsigned)];
    xsnprintf(strport, sizeof(strport), "%u", (unsigned) SYSLOG_PORT);

    query.ai_family = AF_UNSPEC;
    query.ai_socktype = SOCK_DGRAM;

    int err = getaddrinfo(SYSLOG_HOST, strport, &query, &response);
    if (err != 0)
    {
        Log(LOG_LEVEL_INFO,
            "Unable to find syslog_host or service: (%s/%s) %s",
            SYSLOG_HOST, strport, gai_strerror(err));
        if (response != NULL)
        {
            freeaddrinfo(response);
        }
        return;
    }

    for (const struct addrinfo *ap = response; ap != NULL; ap = ap->ai_next)
    {
        /* No DNS lookup, just convert IP address to string. */
        char txtaddr[CF_MAX_IP_LEN] = "";
        getnameinfo(ap->ai_addr, ap->ai_addrlen,
                    txtaddr, sizeof(txtaddr),
                    NULL, 0, NI_NUMERICHOST);
        Log(LOG_LEVEL_VERBOSE,
            "Connect to syslog '%s' = '%s' on port '%s'",
            SYSLOG_HOST, txtaddr, strport);

        int sd = socket(ap->ai_family, ap->ai_socktype, IPPROTO_UDP);
        if (sd == -1)
        {
            Log(LOG_LEVEL_INFO, "Couldn't open a socket. (socket: %s)", GetErrorStr());
            continue;
        }
        else
        {
            const size_t rfc3164_len = 1024;
            char message[rfc3164_len];
            char timebuffer[26];
            pid_t pid = getpid();

            snprintf(message, sizeof(message), "<%i>%.15s %s %s[%d]: %s",
                     log_priority | SYSLOG_FACILITY,
                     cf_strtimestamp_local(now, timebuffer) + 4,
                     VFQNAME, VPREFIX, pid, log_string);
            err = sendto(sd, message, strlen(message),
                         0, ap->ai_addr, ap->ai_addrlen);
            if (err == -1)
            {
                Log(LOG_LEVEL_VERBOSE, "Couldn't send '%s' to syslog server '%s'. (sendto: %s)",
                    message, SYSLOG_HOST, GetErrorStr());
            }
            else
            {
                Log(LOG_LEVEL_VERBOSE, "Syslog message: '%s' to server '%s'", message, SYSLOG_HOST);
            }
            close(sd);
        }
    }

    freeaddrinfo(response);
}
示例#29
0
static int ParseEntityPosixLinux(char **str, acl_entry_t ace, int *is_mask)
{
    struct passwd *pwd;
    struct group *grp;
    acl_tag_t etype;
    size_t idsz;
    id_t id;
    char *ids;
    char *id_end;
    int result = true;
    int i;

    ids = NULL;

// TODO: Support numeric id in addition to (user/group) name ?

// Posix language: tag type, qualifier, permissions

    if (strncmp(*str, "user:"******"*", 2) == 0)
        {
            etype = ACL_USER_OBJ;
            id = 0;
        }
        else
        {
            etype = ACL_USER;
            pwd = getpwnam(ids);

            if (pwd == NULL)
            {
                Log(LOG_LEVEL_ERR, "Couldn't find user id for '%s'. (getpwnnam: %s)", ids, GetErrorStr());
                free(ids);
                return false;
            }

            id = pwd->pw_uid;
        }
    }
    else if (strncmp(*str, "group:", 6) == 0)
    {
        *str += 6;

        // create null-terminated string for entity id
        id_end = index(*str, ':');

        if (id_end == NULL)     // entity id already null-terminated
        {
            idsz = strlen(*str);
        }
        else                    // copy entity-id to new null-terminated string
        {
            idsz = id_end - *str;
        }

        ids = xmalloc(idsz + 1);
        for (i = 0; i < idsz; i++)
            ids[i] = (*str)[i];
        ids[idsz] = '\0';

        *str += idsz;

        // file group
        if (strncmp(ids, "*", 2) == 0)
        {
            etype = ACL_GROUP_OBJ;
            id = 0;             // TODO: Correct file group id ??
        }
        else
        {
            etype = ACL_GROUP;
            grp = getgrnam(ids);

            if (grp == NULL)
            {
                Log(LOG_LEVEL_ERR, "Error looking up group id for %s", ids);
                free(ids);
                return false;
            }

            id = grp->gr_gid;
        }

    }
    else if (strncmp(*str, "all:", 4) == 0)
    {
        *str += 3;
        etype = ACL_OTHER;
    }
    else if (strncmp(*str, "mask:", 5) == 0)
    {
        *str += 4;
        etype = ACL_MASK;
        *is_mask = true;
    }
    else
    {
        Log(LOG_LEVEL_ERR, "ace does not start with user:/group:/all:/mask:");
        return false;
    }

    if (acl_set_tag_type(ace, etype) != 0)
    {
        Log(LOG_LEVEL_ERR, "Could not set ACE tag type. (acl_set_tag_type: %s)", GetErrorStr());
        result = false;
    }
    else if (etype == ACL_USER || etype == ACL_GROUP)
    {
        if ((acl_set_qualifier(ace, &id)) != 0)
        {
            Log(LOG_LEVEL_ERR, "Could not set ACE qualifier. (acl_set_qualifier: %s)", GetErrorStr());
            result = false;
        }
    }

    if (ids != NULL)
    {
        free(ids);
    }

    return result;
}
示例#30
0
文件: verify_files.c 项目: lra/core
static PromiseResult VerifyFilePromise(EvalContext *ctx, char *path, const Promise *pp)
{
    struct stat osb, oslb, dsb;
    CfLock thislock;
    int exists;

    Attributes a = GetFilesAttributes(ctx, pp);

    if (!FileSanityChecks(path, a, pp))
    {
        ClearFilesAttributes(&a);
        return PROMISE_RESULT_NOOP;
    }

    EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_THIS, "promiser", path, CF_DATA_TYPE_STRING, "source=promise");

    thislock = AcquireLock(ctx, path, VUQNAME, CFSTARTTIME, a.transaction, pp, false);
    if (thislock.lock == NULL)
    {
        ClearFilesAttributes(&a);
        return PROMISE_RESULT_SKIPPED;
    }

    LoadSetuid();

    PromiseResult result = PROMISE_RESULT_NOOP;
    if (lstat(path, &oslb) == -1)       /* Careful if the object is a link */
    {
        if ((a.create) || (a.touch))
        {
            if (!CfCreateFile(ctx, path, pp, a, &result))
            {
                goto exit;
            }
            else
            {
                exists = (lstat(path, &oslb) != -1);
            }
        }

        exists = false;
    }
    else
    {
        if ((a.create) || (a.touch))
        {
            cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "File '%s' exists as promised", path);
        }
        exists = true;
    }

    if ((a.havedelete) && (!exists))
    {
        cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "File '%s' does not exist as promised", path);
        goto exit;
    }

    if (!a.havedepthsearch)     /* if the search is trivial, make sure that we are in the parent dir of the leaf */
    {
        char basedir[CF_BUFSIZE];

        Log(LOG_LEVEL_DEBUG, "Direct file reference '%s', no search implied", path);
        snprintf(basedir, sizeof(basedir), "%s", path);

        if (strcmp(ReadLastNode(basedir), ".") == 0)
        {
            // Handle /.  notation for deletion of directories
            ChopLastNode(basedir);
            ChopLastNode(path);
        }

        ChopLastNode(basedir);
        if (safe_chdir(basedir))
        {
            Log(LOG_LEVEL_ERR, "Failed to chdir into '%s'. (chdir: '%s')", basedir, GetErrorStr());
        }
    }

    /* If file or directory exists but it is not selected by body file_select
     * (if we have one) then just exit. But continue if it's a directory and
     * depth_search is on, so that we can file_select into it. */
    if (exists
        && (a.haveselect && !SelectLeaf(ctx, path, &oslb, a.select))
        && !(a.havedepthsearch && S_ISDIR(oslb.st_mode)))
    {
        goto exit;
    }

    if (stat(path, &osb) == -1)
    {
        if ((a.create) || (a.touch))
        {
            if (!CfCreateFile(ctx, path, pp, a, &result))
            {
                goto exit;
            }
            else
            {
                exists = true;
            }
        }
        else
        {
            exists = false;
        }
    }
    else
    {
        if (!S_ISDIR(osb.st_mode))
        {
            if (a.havedepthsearch)
            {
                Log(LOG_LEVEL_WARNING,
                    "depth_search (recursion) is promised for a base object '%s' that is not a directory",
                      path);
                goto exit;
            }
        }

        exists = true;
    }

    if (a.link.link_children)
    {
        if (stat(a.link.source, &dsb) != -1)
        {
            if (!S_ISDIR(dsb.st_mode))
            {
                Log(LOG_LEVEL_ERR, "Cannot promise to link the children of '%s' as it is not a directory!",
                      a.link.source);
                goto exit;
            }
        }
    }

/* Phase 1 - */

    if (exists && ((a.havedelete) || (a.haverename) || (a.haveperms) || (a.havechange) || (a.transformer)))
    {
        lstat(path, &oslb);     /* if doesn't exist have to stat again anyway */

        DepthSearch(ctx, path, &oslb, 0, a, pp, oslb.st_dev, &result);

        /* normally searches do not include the base directory */

        if (a.recursion.include_basedir)
        {
            int save_search = a.havedepthsearch;

            /* Handle this node specially */

            a.havedepthsearch = false;
            DepthSearch(ctx, path, &oslb, 0, a, pp, oslb.st_dev, &result);
            a.havedepthsearch = save_search;
        }
        else
        {
            /* unless child nodes were repaired, set a promise kept class */
            if (!IsDefinedClass(ctx, "repaired"))
            {
                cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Basedir '%s' not promising anything", path);
            }
        }
    }

/* Phase 2a - copying is potentially threadable if no followup actions */

    if (a.havecopy)
    {
        result = PromiseResultUpdate(result, ScheduleCopyOperation(ctx, path, a, pp));
    }

/* Phase 2b link after copy in case need file first */

    if ((a.havelink) && (a.link.link_children))
    {
        result = PromiseResultUpdate(result, ScheduleLinkChildrenOperation(ctx, path, a.link.source, 1, a, pp));
    }
    else if (a.havelink)
    {
        result = PromiseResultUpdate(result, ScheduleLinkOperation(ctx, path, a.link.source, a, pp));
    }

/* Phase 3 - content editing */

    if (a.haveedit)
    {
        if (exists)
        {
            result = PromiseResultUpdate(result, ScheduleEditOperation(ctx, path, a, pp));
        }
        else
        {
            cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Promised to edit '%s', but file does not exist", path);
            result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL);
        }
    }

// Once more in case a file has been created as a result of editing or copying

    exists = (stat(path, &osb) != -1);

    if (exists && (S_ISREG(osb.st_mode))
        && (!a.haveselect || SelectLeaf(ctx, path, &osb, a.select)))
    {
        VerifyFileLeaf(ctx, path, &osb, a, pp, &result);
    }

    if (!exists && a.havechange)
    {
        cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a, "Promised to monitor '%s' for changes, but file does not exist", path);
        result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL);
    }

exit:

    if (AttrHasNoAction(a))
    {
        Log(LOG_LEVEL_INFO,
            "No action was requested for file '%s'. Maybe a typo in the policy?", path);
    }

    SaveSetuid();
    YieldCurrentLock(thislock);
    ClearFilesAttributes(&a);

    return result;
}