bool _qd_policy_approve_link_name_tree()

in src/policy.c [894:1032]


bool _qd_policy_approve_link_name_tree(const char *username, const char *allowed, const char *proposed,
                                       qd_parse_tree_t *tree)
{
    // Verify string sizes are usable
    size_t proposed_len = strlen(proposed);
    if (proposed_len == 0) {
        // degenerate case of blank proposed name being opened. will never match anything.
        return false;
    }
    size_t a_len = strlen(allowed);
    if (a_len == 0) {
        // no names in 'allowed'.
        return false;
    }

    if (!username)
        username = "";

    size_t username_len = strlen(username);
    size_t usersubst_len = strlen(user_subst_key);

    // make a writable, disposable copy of the csv string
    char * dup = strdup(allowed);
    if (!dup) {
        return false;
    }
    char * dupend = dup + strlen(dup);
    char * pch = dup;

    // get a scratch buffer for writing temporary match strings
    char * pName = (char *)malloc(QPALN_SIZE);
    if (!pName) {
        free(dup);
        return false;
    }
    size_t pName_sz = QPALN_SIZE;

    bool result = false;

    while (pch < dupend) {
        // the tuple strings
        char  *pChar, *pS1, *pS2;
        size_t sChar,  sS1,  sS2;

        // extract control field
        sChar = strcspn(pch, QPALN_COMMA_SEP);
        if (sChar != 1) { assert(false); break;}
        pChar = pch;
        pChar[sChar] = '\0';
        pch += sChar + 1;
        if (pch >= dupend) { assert(false); break; }

        // extract prefix field S1
        sS1 = strcspn(pch, QPALN_COMMA_SEP);
        pS1 = pch;
        pS1[sS1] = '\0';
        pch += sS1 + 1;
        if (pch > dupend) { assert(false); break; }

        // extract suffix field S2
        sS2 = strcspn(pch, QPALN_COMMA_SEP);
        pS2 = pch;
        pch += sS2 + 1;
        pS2[sS2] = '\0';

        // compute size of generated string and make sure
        // temporary buffer is big enough to hold it.
        size_t sName = proposed_len + usersubst_len + 1;
        if (sName > pName_sz) {
            size_t newSize = sName + QPALN_SIZE;
            char * newPtr = (char *)realloc(pName, newSize);
            if (!newPtr) {
                break;
            }
            pName = newPtr;
            pName_sz = newSize;
        }

        // From the rule clause construct what the rule is allowing
        // given the user name associated with this request.
        if (*pChar == *user_subst_i_absent) {
            // Substitution spec is absent. The search string is the literal
            // S1 in the rule.
            snprintf(pName, sName, "%s", proposed);
        }
        else if (*pChar == *user_subst_i_prefix) {
            // Substitution spec is prefix.
            if (strncmp(proposed, username, username_len) != 0)
                continue; // Denied. Proposed does not have username prefix.
            // Check that username is not part of a larger token.
            if (username_len == proposed_len) {
                // If username is the whole link name then allow if lookup is ok
            } else {
                // Proposed is longer than username. Make sure that proposed
                // is delimited after user name.
                if (!is_token_sep(proposed[username_len])) {
                    continue; // denied. proposed has username prefix it it not a delimited user name
                }
            }
            snprintf(pName, sName, "%s%s", user_subst_key, proposed + username_len);
        }
        else if (*pChar == *user_subst_i_embed) {
            assert(false); // not supported
        }
        else if (*pChar == *user_subst_i_suffix) {
            // Check that link name has username suffix
            if (username_len > proposed_len) {
                continue; // denied. proposed name is too short to hold username
            } else {
                //---
                // if (username_len == proposed_len) { ... }
                // unreachable code. substitution-only rule clause is handled by prefix
                //---
                if (!is_token_sep(proposed[proposed_len - username_len - 1])) {
                    continue; // denied. proposed suffix it it not a delimited user name
                }
                if (strncmp(&proposed[proposed_len - username_len], username, username_len) != 0) {
                    continue; // denied. username is not the suffix
                }
            }
            pName[0] = '\0';
            strncat(pName, proposed, proposed_len - username_len);
            strcat(pName, user_subst_key);
        }
        else {
            assert(false);
            break;
        }

        void * unused_payload = 0;
        result = qd_parse_tree_retrieve_match_str(tree, pName, &unused_payload);
        if (result)
            break;
    }
    free(pName);
    free(dup);

    return result;
}