Esempio n. 1
0
/* Only search in PATH_STRLIST because it makes sense to search longest prefix
 * for paths. */
static void test_StrList_SearchLongestPrefix()
{
    /* REMINDER: PATH_STRINGS[] =
       { " ", "  ", "/", "/path", "/path/to/file.name", "blah", "waza" }; */

    size_t ret, ret2, ret3;

    /* These searches all search for  "/path", since length is the same. */
    ret  = StrList_SearchLongestPrefix(PATH_SL, "/path", 0, '/', true);
    ret2 = StrList_SearchLongestPrefix(PATH_SL, "/path/", 5, '/', true);
    ret3 = StrList_SearchLongestPrefix(PATH_SL, "/path/to/file.name", 5, '/', true);
    assert_string_equal(StrList_At(PATH_SL, ret), "/path");
    assert_string_equal(StrList_At(PATH_SL, ret2), "/path");
    assert_string_equal(StrList_At(PATH_SL, ret3), "/path");

    /* Searching for "/path/" does not bring up "/path", but "/", since
     * directories *must* have a trailing slash. */
    ret = StrList_SearchLongestPrefix(PATH_SL, "/path/", 0, '/', true);
    assert_string_equal(StrList_At(PATH_SL, ret), "/");

    ret = StrList_SearchLongestPrefix(PATH_SL, "/path.json", 0, '/', true);
    assert_string_equal(StrList_At(PATH_SL, ret), "/");


    /* We insert a couple more directories and sort again. */
    StrList_Append(&PATH_SL, "/path/to/file.namewhatever/whatever");
    StrList_Append(&PATH_SL, "/path/to/file.name/whatever/");
    StrList_Append(&PATH_SL, "/path/to/");
    StrList_Sort(PATH_SL, string_Compare);

    ret = StrList_SearchLongestPrefix(PATH_SL, "/path/to/file.name",
                                      0, '/', true);
    assert_string_equal(StrList_At(PATH_SL, ret), "/path/to/file.name");

    ret = StrList_SearchLongestPrefix(PATH_SL, "/path/to/file",
                                      0, '/', true);
    assert_string_equal(StrList_At(PATH_SL, ret), "/path/to/");

    ret = StrList_SearchLongestPrefix(PATH_SL, "/path/to/file.name/whatever/blah",
                                      0, '/', true);
    assert_string_equal(StrList_At(PATH_SL, ret), "/path/to/file.name/whatever/");

    ret = StrList_SearchLongestPrefix(PATH_SL, "/path/to/",
                                      0, '/', true);
    assert_string_equal(StrList_At(PATH_SL, ret), "/path/to/");
}
Esempio n. 2
0
/**
 * The "roles" access promise is for remote class activation by means of
 * cf-runagent -D:
 *
 *     pp->promiser is a regex to match classes.
 *     pp->conlist  is an slist of usernames.
 */
static void KeepServerRolePromise(EvalContext *ctx, const Promise *pp)
{
    size_t pos = acl_SortedInsert(&roles_acl, pp->promiser);
    if (pos == (size_t) -1)
    {
        /* Should never happen, besides when allocation fails. */
        Log(LOG_LEVEL_CRIT, "acl_Insert: %s", GetErrorStr());
        exit(255);
    }

    size_t i = SeqLength(pp->conlist);
    while (i > 0)
    {
        i--;
        Constraint *cp = SeqAt(pp->conlist, i);
        char const * const authorizer =
            CF_REMROLE_BODIES[REMOTE_ROLE_AUTHORIZE].lval;

        if (strcmp(cp->lval, authorizer) == 0)
        {
            if (cp->rval.type != RVAL_TYPE_LIST)
            {
                Log(LOG_LEVEL_ERR,
                    "Right-hand side of authorize promise for '%s' should be a list",
                    pp->promiser);
            }
            else if (IsDefinedClass(ctx, cp->classes))
            {
                for (const Rlist *rp = cp->rval.item; rp != NULL; rp = rp->next)
                {
                    /* The "roles" access promise currently only supports
                     * listing usernames to admit access to, nothing more. */
                    struct resource_acl *racl = &roles_acl->acls[pos];
                    size_t zret = StrList_Append(&racl->admit.usernames,
                                                 RlistScalarValue(rp));
                    if (zret == (size_t) -1)
                    {
                        /* Should never happen, besides when allocation fails. */
                        Log(LOG_LEVEL_CRIT, "StrList_Append: %s", GetErrorStr());
                        exit(255);
                    }
                }
            }
        }
        else if (strcmp(cp->lval, "comment") != 0 &&
                 strcmp(cp->lval, "handle") != 0 &&
                 /* Are there other known list constraints ? if not, skip this: */
                 cp->rval.type != RVAL_TYPE_LIST)
        {
            Log(LOG_LEVEL_WARNING,
                "Unrecognised promise '%s' for %s",
                cp->lval, pp->promiser);
        }
    }
}
Esempio n. 3
0
static size_t racl_SmartAppend(struct admitdeny_acl *ad, const char *entry)
{
    size_t ret;

    switch (AdmitType(entry))
    {

    case ADMIT_TYPE_IP:
        /* TODO convert IP string to binary representation. */
        ret = StrList_Append(&ad->ips, entry);
        break;

    case ADMIT_TYPE_KEY:
        ret = StrList_Append(&ad->keys, entry);
        break;

    case ADMIT_TYPE_HOSTNAME:
        /* TODO clean up possible regex, if it starts with ".*"
         * then store two entries: entry, and *dot*entry. */
        ret = StrList_Append(&ad->hostnames, entry);

        /* If any hostname rule is present, we set a global flag to turn on
         * reverse DNS lookup in the new protocol. */
        if (!NEED_REVERSE_LOOKUP)
        {
            Log(LOG_LEVEL_INFO,
                "Found hostname admit/deny access_rules, "
                "turning on reverse DNS lookups on every connection");
            NEED_REVERSE_LOOKUP = true;
        }

        break;

    default:
        Log(LOG_LEVEL_WARNING,
            "Access rule 'admit: %s' is not IP, hostname or key, ignoring",
            entry);
        ret = (size_t) -1;
    }

    return ret;
}
Esempio n. 4
0
static size_t racl_SmartAppend(struct admitdeny_acl *ad, const char *entry)
{
    size_t ret;

    switch (AdmitType(entry))
    {

    case ADMIT_TYPE_IP:
        /* TODO convert IP string to binary representation. */
        ret = StrList_Append(&ad->ips, entry);
        break;

    case ADMIT_TYPE_KEY:
        ret = StrList_Append(&ad->keys, entry);
        break;

    case ADMIT_TYPE_HOSTNAME:
        ret = DeRegexify(&ad->hostnames, entry);

        /* If any hostname rule got added,
         * turn on reverse DNS lookup in the new protocol. */
        if (ret != (size_t) -1)
        {
            TurnOnReverseLookups();
        }

        break;

    default:
        Log(LOG_LEVEL_WARNING,
            "Access rule 'admit: %s' is not IP, hostname or key, ignoring",
            entry);
        ret = (size_t) -1;
    }

    return ret;
}
Esempio n. 5
0
static StrList *init_strlist(char ** strings, size_t strings_len,
                             size_t *insert_order)
{
    StrList *sl = calloc(1, sizeof(*sl));
    /* Insert in random order. */
    for (size_t i = 0; i < strings_len; i++)
    {
        size_t ret = StrList_Append(&sl, strings[ insert_order[i] ]);
        assert_int_equal(ret, i);
        assert_string_equal(StrList_At(sl, i), strings[ insert_order[i] ]);
    }
    assert_int_equal(StrList_Len(sl), strings_len);
    StrList_Finalise(&sl);

    return sl;
}
Esempio n. 6
0
/**
 * Add access rules to the given ACL #acl according to the constraints in the
 * particular access promise.
 *
 * For legacy reasons (non-TLS connections), build also the #ap (access Auth)
 * and #dp (deny Auth).
 */
static void AccessPromise_AddAccessConstraints(const EvalContext *ctx,
                                               const Promise *pp,
                                               struct resource_acl *racl,
                                               Auth *ap, Auth *dp)
{
    for (size_t i = 0; i < SeqLength(pp->conlist); i++)
    {
        const Constraint *cp = SeqAt(pp->conlist, i);
        size_t ret = -2;

        if (!IsDefinedClass(ctx, cp->classes))
        {
            continue;
        }

        switch (cp->rval.type)
        {
#define IsAccessBody(e) (strcmp(cp->lval, CF_REMACCESS_BODIES[e].lval) == 0)

        case RVAL_TYPE_SCALAR:

            if (IsAccessBody(REMOTE_ACCESS_IFENCRYPTED))
            {
                ap->encrypt = BooleanFromString(cp->rval.item);
            }
            else if (IsAccessBody(REMOTE_ACCESS_SHORTCUT))
            {
                const char *shortcut = cp->rval.item;

                if (strchr(shortcut, FILE_SEPARATOR) != NULL)
                {
                    Log(LOG_LEVEL_ERR,
                        "slashes are forbidden in ACL shortcut: %s",
                        shortcut);
                }
                else if (StringMapHasKey(SV.path_shortcuts, shortcut))
                {
                    Log(LOG_LEVEL_WARNING,
                        "Already existing shortcut for path '%s' was replaced",
                        pp->promiser);
                }
                else
                {
                    StringMapInsert(SV.path_shortcuts,
                                    xstrdup(shortcut), xstrdup(pp->promiser));

                    Log(LOG_LEVEL_DEBUG, "Added shortcut '%s' for path: %s",
                        shortcut, pp->promiser);
                }
            }
            break;

        case RVAL_TYPE_LIST:

            for (const Rlist *rp = (const Rlist *) cp->rval.item;
                 rp != NULL; rp = rp->next)
            {
                /* TODO keys, ips, hostnames are valid such strings. */

                if (IsAccessBody(REMOTE_ACCESS_ADMITIPS))
                {
                    ret = StrList_Append(&racl->admit.ips, RlistScalarValue(rp));
                    PrependItem(&(ap->accesslist), RlistScalarValue(rp), NULL);
                }
                else if (IsAccessBody(REMOTE_ACCESS_DENYIPS))
                {
                    ret = StrList_Append(&racl->deny.ips, RlistScalarValue(rp));
                    PrependItem(&(dp->accesslist), RlistScalarValue(rp), NULL);
                }
                else if (IsAccessBody(REMOTE_ACCESS_ADMITHOSTNAMES))
                {
                    ret = StrList_Append(&racl->admit.hostnames, RlistScalarValue(rp));
                    /* If any hostname rule got added,
                     * turn on reverse DNS lookup in the new protocol. */
                    if (ret != (size_t) -1)
                    {
                        TurnOnReverseLookups();
                    }
                    NewHostToOldACL(ap, RlistScalarValue(rp));
                }
                else if (IsAccessBody(REMOTE_ACCESS_DENYHOSTNAMES))
                {
                    ret = StrList_Append(&racl->deny.hostnames, RlistScalarValue(rp));
                    /* If any hostname rule got added,
                     * turn on reverse DNS lookup in the new protocol. */
                    if (ret != (size_t) -1)
                    {
                        TurnOnReverseLookups();
                    }
                    NewHostToOldACL(dp, RlistScalarValue(rp));
                }
                else if (IsAccessBody(REMOTE_ACCESS_ADMITKEYS))
                {
                    ret = StrList_Append(&racl->admit.keys, RlistScalarValue(rp));
                }
                else if (IsAccessBody(REMOTE_ACCESS_DENYKEYS))
                {
                    ret = StrList_Append(&racl->deny.keys, RlistScalarValue(rp));
                }
                /* Legacy stuff */
                else if (IsAccessBody(REMOTE_ACCESS_ADMIT))
                {
                    ret = racl_SmartAppend(&racl->admit, RlistScalarValue(rp));
                    PrependItem(&(ap->accesslist), RlistScalarValue(rp), NULL);
                }
                else if (IsAccessBody(REMOTE_ACCESS_DENY))
                {
                    ret = racl_SmartAppend(&racl->deny, RlistScalarValue(rp));
                    PrependItem(&(dp->accesslist), RlistScalarValue(rp), NULL);
                }
                else if (IsAccessBody(REMOTE_ACCESS_MAPROOT))
                {
                    PrependItem(&(ap->maproot), RlistScalarValue(rp), NULL);
                }
            }

            if (ret == (size_t) -1)
            {
                /* Should never happen, besides when allocation fails. */
                Log(LOG_LEVEL_CRIT, "StrList_Append: %s", GetErrorStr());
                exit(255);
            }

            break;

        default:
            UnexpectedError("Unknown constraint type!");
            break;

#undef IsAccessBody
        }
    }

    StrList_Finalise(&racl->admit.ips);
    StrList_Sort(racl->admit.ips, string_Compare);

    StrList_Finalise(&racl->admit.hostnames);
    StrList_Sort(racl->admit.hostnames, string_CompareFromEnd);

    StrList_Finalise(&racl->admit.keys);
    StrList_Sort(racl->admit.keys, string_Compare);

    StrList_Finalise(&racl->deny.ips);
    StrList_Sort(racl->deny.ips, string_Compare);

    StrList_Finalise(&racl->deny.hostnames);
    StrList_Sort(racl->deny.hostnames, string_CompareFromEnd);

    StrList_Finalise(&racl->deny.keys);
    StrList_Sort(racl->deny.keys, string_Compare);
}
Esempio n. 7
0
/* Map old-style regex-or-hostname to new-style host-or-domain.
 *
 * Old-style ACLs could include regexes to be matched against host
 * names; but new-style ones only support sub-domain matching.  If the
 * old-style host regex looks like ".*\.sub\.domain\.tld" we can take
 * it in as ".sub.domain.tld"; otherwise, we can only really map exact
 * match hostnames.  However, we know some old policy (including our
 * own masterfiles) had cases of .*sub.domain.tld and it's possible
 * that someone might include a new-style .sub.domain.tld by mistake
 * in an (old-style) accept list; so cope with these cases, too.
 *
 * @param sl The string-list to which to add entries.
 * @param host The name-or-regex to add to the ACL.
 * @return An index at which an entry was added to the list (there may
 * be another), or -1 if nothing added.
 */
static size_t DeRegexify(StrList **sl, const char *host)
{
    if (IsRegex(host))
    {
        if (host[strcspn(host, "({[|+?]})")] != '\0')
        {
            return -1; /* Not a regex we can sensibly massage; discard. */
        }
        bool skip[2] = { false, false }; /* { domain, host } passes below */
        const char *name = host;
        if (name[0] == '^') /* Was always implicit; but read as hint to intent. */
        {
            /* Default to skipping domain-form if anchored explicitly: */
            skip[0] = true; /* Over-ridden below if followed by .* of course. */
            name++;
        }
        if (StringStartsWith(name, ".*"))
        {
            skip[0] = false; /* Domain-form should match */
            name += 2;
        }
        if (StringStartsWith(name, "\\."))
        {
            /* Skip host-form, as the regex definitely wants something
             * before the given name. */
            skip[1] = true;
            name += 2;
        }
        if (strchr(name, '*') != NULL)
        {
            /* Can't handle a * later than the preamble. */
            return (size_t) -1;
        }

        if (name > host || NULL != strchr(host, '\\'))
        {
            /* 2: leading '.' and final '\0' */
            char copy[2 + strlen(name)], *c = copy;
            c++[0] = '.'; /* For domain-form; and copy+1 gives host-form. */
            /* Now copy the rest of the name, de-regex-ifying as we go: */
            for (const char *p = name; p[0] != '\0'; p++)
            {
                if (p[0] == '\\')
                {
                    p++;
                    if (p[0] != '.')
                    {
                        /* Regex includes a non-dot escape */
                        return (size_t) -1;
                    }
                }
#if 0
                else if (p[0] == '.')
                {
                    /* In principle, this is a special character; but
                     * it may just be an unescaped dot, so let it be. */
                }
#endif
                c++[0] = p[0];
            }
            assert(c < copy + sizeof(copy));
            c[0] = '\0';

            /* Now, for host then domain, add entry if suitable */
            int pass = 2;
            size_t ret = -1;
            while (pass > 0)
            {
                pass--;
                if (!skip[pass]) /* pass 0 is domain, pass 1 is host */
                {
                    ret = StrList_Append(sl, copy + pass);
                }
            }
            return ret;
        }
        /* IsRegex() but is actually so boring it's just a name ! */
    }
    /* Just a simple host name. */

    return StrList_Append(sl, host);
}