Blame src/token.c

Packit Service 31306d
/*
Packit Service 31306d
 * token.c - Token list handling functions
Packit Service 31306d
 *
Packit Service 31306d
 * This file is part of the SSH Library
Packit Service 31306d
 *
Packit Service 31306d
 * Copyright (c) 2003-2008 by Aris Adamantiadis
Packit Service 31306d
 * Copyright (c) 2019 by Anderson Toshiyuki Sasaki - Red Hat, Inc.
Packit Service 31306d
 *
Packit Service 31306d
 * The SSH Library is free software; you can redistribute it and/or modify
Packit Service 31306d
 * it under the terms of the GNU Lesser General Public License as published by
Packit Service 31306d
 * the Free Software Foundation; either version 2.1 of the License, or (at your
Packit Service 31306d
 * option) any later version.
Packit Service 31306d
 *
Packit Service 31306d
 * The SSH Library is distributed in the hope that it will be useful, but
Packit Service 31306d
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit Service 31306d
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
Packit Service 31306d
 * License for more details.
Packit Service 31306d
 *
Packit Service 31306d
 * You should have received a copy of the GNU Lesser General Public License
Packit Service 31306d
 * along with the SSH Library; see the file COPYING.  If not, write to
Packit Service 31306d
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
Packit Service 31306d
 * MA 02111-1307, USA.
Packit Service 31306d
 */
Packit Service 31306d
Packit Service 31306d
#include "config.h"
Packit Service 31306d
Packit Service 31306d
#include <stdio.h>
Packit Service 31306d
#include <string.h>
Packit Service 31306d
#include <stdbool.h>
Packit Service 31306d
Packit Service 31306d
#include "libssh/priv.h"
Packit Service 31306d
#include "libssh/token.h"
Packit Service 31306d
Packit Service 31306d
/**
Packit Service 31306d
 * @internal
Packit Service 31306d
 *
Packit Service 31306d
 * @brief Free the given tokens list structure. The used buffer is overwritten
Packit Service 31306d
 * with zeroes before freed.
Packit Service 31306d
 *
Packit Service 31306d
 * @param[in] tokens    The pointer to a structure to be freed;
Packit Service 31306d
 */
Packit Service 31306d
void ssh_tokens_free(struct ssh_tokens_st *tokens)
Packit Service 31306d
{
Packit Service 31306d
    int i;
Packit Service 31306d
    if (tokens == NULL) {
Packit Service 31306d
        return;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    if (tokens->tokens != NULL) {
Packit Service 31306d
        for (i = 0; tokens->tokens[i] != NULL; i++) {
Packit Service 31306d
            explicit_bzero(tokens->tokens[i], strlen(tokens->tokens[i]));
Packit Service 31306d
        }
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    SAFE_FREE(tokens->buffer);
Packit Service 31306d
    SAFE_FREE(tokens->tokens);
Packit Service 31306d
    SAFE_FREE(tokens);
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
/**
Packit Service 31306d
 * @internal
Packit Service 31306d
 *
Packit Service 31306d
 * @brief Split a given string on the given separator character. The returned
Packit Service 31306d
 * structure holds an array of pointers (tokens) pointing to the obtained
Packit Service 31306d
 * parts and a buffer where all the content of the list is stored. The last
Packit Service 31306d
 * element of the array will always be set as NULL.
Packit Service 31306d
 *
Packit Service 31306d
 * @param[in] chain         The string to split
Packit Service 31306d
 * @param[in] separator     The character used to separate the tokens.
Packit Service 31306d
 *
Packit Service 31306d
 * @return  A newly allocated tokens list structure; NULL in case of error.
Packit Service 31306d
 */
Packit Service 31306d
struct ssh_tokens_st *ssh_tokenize(const char *chain, char separator)
Packit Service 31306d
{
Packit Service 31306d
Packit Service 31306d
    struct ssh_tokens_st *tokens = NULL;
Packit Service 31306d
    size_t num_tokens = 1, i = 1;
Packit Service 31306d
Packit Service 31306d
    char *found, *c;
Packit Service 31306d
Packit Service 31306d
    if (chain == NULL) {
Packit Service 31306d
        return NULL;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    tokens = calloc(1, sizeof(struct ssh_tokens_st));
Packit Service 31306d
    if (tokens == NULL) {
Packit Service 31306d
        return NULL;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    tokens->buffer= strdup(chain);
Packit Service 31306d
    if (tokens->buffer == NULL) {
Packit Service 31306d
        goto error;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    c = tokens->buffer;
Packit Service 31306d
    do {
Packit Service 31306d
        found = strchr(c, separator);
Packit Service 31306d
        if (found != NULL) {
Packit Service 31306d
            c = found + 1;
Packit Service 31306d
            num_tokens++;
Packit Service 31306d
        }
Packit Service 31306d
    } while(found != NULL);
Packit Service 31306d
Packit Service 31306d
    /* Allocate tokens list */
Packit Service 31306d
    tokens->tokens = calloc(num_tokens + 1, sizeof(char *));
Packit Service 31306d
    if (tokens->tokens == NULL) {
Packit Service 31306d
        goto error;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    /* First token starts in the beginning of the chain */
Packit Service 31306d
    tokens->tokens[0] = tokens->buffer;
Packit Service 31306d
    c = tokens->buffer;
Packit Service 31306d
Packit Service 31306d
    for (i = 1; i < num_tokens; i++) {
Packit Service 31306d
        /* Find next separator */
Packit Service 31306d
        found = strchr(c, separator);
Packit Service 31306d
        if (found == NULL) {
Packit Service 31306d
            break;
Packit Service 31306d
        }
Packit Service 31306d
Packit Service 31306d
        /* Replace it with a string terminator */
Packit Service 31306d
        *found = '\0';
Packit Service 31306d
Packit Service 31306d
        /* The next token starts in the next byte */
Packit Service 31306d
        c = found + 1;
Packit Service 31306d
Packit Service 31306d
        /* If we did not reach the end of the chain yet, set the next token */
Packit Service 31306d
        if (*c != '\0') {
Packit Service 31306d
            tokens->tokens[i] = c;
Packit Service 31306d
        } else {
Packit Service 31306d
            break;
Packit Service 31306d
        }
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    return tokens;
Packit Service 31306d
Packit Service 31306d
error:
Packit Service 31306d
    ssh_tokens_free(tokens);
Packit Service 31306d
    return NULL;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
/**
Packit Service 31306d
 * @internal
Packit Service 31306d
 *
Packit Service 31306d
 * @brief Given two strings, the first containing a list of available tokens and
Packit Service 31306d
 * the second containing a list of tokens to be searched ordered by preference,
Packit Service 31306d
 * returns a copy of the first preferred token present in the available list.
Packit Service 31306d
 *
Packit Service 31306d
 * @param[in] available_list    The list of available tokens
Packit Service 31306d
 * @param[in] preferred_list    The list of tokens to search, ordered by
Packit Service 31306d
 * preference
Packit Service 31306d
 *
Packit Service 31306d
 * @return  A newly allocated copy of the token if found; NULL otherwise
Packit Service 31306d
 */
Packit Service 31306d
char *ssh_find_matching(const char *available_list,
Packit Service 31306d
                        const char *preferred_list)
Packit Service 31306d
{
Packit Service 31306d
    struct ssh_tokens_st *a_tok = NULL, *p_tok = NULL;
Packit Service 31306d
Packit Service 31306d
    int i, j;
Packit Service 31306d
    char *ret = NULL;
Packit Service 31306d
Packit Service 31306d
    if ((available_list == NULL) || (preferred_list == NULL)) {
Packit Service 31306d
        return NULL;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    a_tok = ssh_tokenize(available_list, ',');
Packit Service 31306d
    if (a_tok == NULL) {
Packit Service 31306d
        return NULL;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    p_tok = ssh_tokenize(preferred_list, ',');
Packit Service 31306d
    if (p_tok == NULL) {
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    for (i = 0; p_tok->tokens[i]; i++) {
Packit Service 31306d
        for (j = 0; a_tok->tokens[j]; j++) {
Packit Service 31306d
            if (strcmp(a_tok->tokens[j], p_tok->tokens[i]) == 0) {
Packit Service 31306d
                ret = strdup(a_tok->tokens[j]);
Packit Service 31306d
                goto out;
Packit Service 31306d
            }
Packit Service 31306d
        }
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
out:
Packit Service 31306d
    ssh_tokens_free(a_tok);
Packit Service 31306d
    ssh_tokens_free(p_tok);
Packit Service 31306d
    return ret;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
/**
Packit Service 31306d
 * @internal
Packit Service 31306d
 *
Packit Service 31306d
 * @brief Given two strings, the first containing a list of available tokens and
Packit Service 31306d
 * the second containing a list of tokens to be searched ordered by preference,
Packit Service 31306d
 * returns a list of all matching tokens ordered by preference.
Packit Service 31306d
 *
Packit Service 31306d
 * @param[in] available_list    The list of available tokens
Packit Service 31306d
 * @param[in] preferred_list    The list of tokens to search, ordered by
Packit Service 31306d
 * preference
Packit Service 31306d
 *
Packit Service 31306d
 * @return  A newly allocated string containing the list of all matching tokens;
Packit Service 31306d
 * NULL otherwise
Packit Service 31306d
 */
Packit Service 31306d
char *ssh_find_all_matching(const char *available_list,
Packit Service 31306d
                            const char *preferred_list)
Packit Service 31306d
{
Packit Service 31306d
    struct ssh_tokens_st *a_tok = NULL, *p_tok = NULL;
Packit Service 31306d
    int i, j;
Packit Service 31306d
    char *ret = NULL;
Packit Service 31306d
    size_t max, len, pos = 0;
Packit Service 31306d
    int match;
Packit Service 31306d
Packit Service 31306d
    if ((available_list == NULL) || (preferred_list == NULL)) {
Packit Service 31306d
        return NULL;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    max = MAX(strlen(available_list), strlen(preferred_list));
Packit Service 31306d
Packit Service 31306d
    ret = calloc(1, max + 1);
Packit Service 31306d
    if (ret == NULL) {
Packit Service 31306d
        return NULL;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    a_tok = ssh_tokenize(available_list, ',');
Packit Service 31306d
    if (a_tok == NULL) {
Packit Service 31306d
        SAFE_FREE(ret);
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    p_tok = ssh_tokenize(preferred_list, ',');
Packit Service 31306d
    if (p_tok == NULL) {
Packit Service 31306d
        SAFE_FREE(ret);
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    for (i = 0; p_tok->tokens[i] ; i++) {
Packit Service 31306d
        for (j = 0; a_tok->tokens[j]; j++) {
Packit Service 31306d
            match = !strcmp(a_tok->tokens[j], p_tok->tokens[i]);
Packit Service 31306d
            if (match) {
Packit Service 31306d
                if (pos != 0) {
Packit Service 31306d
                    ret[pos] = ',';
Packit Service 31306d
                    pos++;
Packit Service 31306d
                }
Packit Service 31306d
Packit Service 31306d
                len = strlen(a_tok->tokens[j]);
Packit Service 31306d
                memcpy(&ret[pos], a_tok->tokens[j], len);
Packit Service 31306d
                pos += len;
Packit Service 31306d
                ret[pos] = '\0';
Packit Service 31306d
            }
Packit Service 31306d
        }
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    if (ret[0] == '\0') {
Packit Service 31306d
        SAFE_FREE(ret);
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
out:
Packit Service 31306d
    ssh_tokens_free(a_tok);
Packit Service 31306d
    ssh_tokens_free(p_tok);
Packit Service 31306d
    return ret;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
/**
Packit Service 31306d
 * @internal
Packit Service 31306d
 *
Packit Service 31306d
 * @brief Given a string containing a list of elements, remove all duplicates
Packit Service 31306d
 * and return in a newly allocated string.
Packit Service 31306d
 *
Packit Service 31306d
 * @param[in] list  The list to be freed of duplicates
Packit Service 31306d
 *
Packit Service 31306d
 * @return  A newly allocated copy of the string free of duplicates; NULL in
Packit Service 31306d
 * case of error.
Packit Service 31306d
 */
Packit Service 31306d
char *ssh_remove_duplicates(const char *list)
Packit Service 31306d
{
Packit Service 31306d
    struct ssh_tokens_st *tok = NULL;
Packit Service 31306d
Packit Service 31306d
    size_t i, j, num_tokens, max_len;
Packit Service 31306d
    char *ret = NULL;
Packit Service 31306d
    bool *should_copy = NULL, need_comma = false;
Packit Service 31306d
Packit Service 31306d
    if (list == NULL) {
Packit Service 31306d
        return NULL;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    /* The maximum number of tokens is the size of the list */
Packit Service 31306d
    max_len = strlen(list);
Packit Service 31306d
    if (max_len == 0) {
Packit Service 31306d
        return NULL;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    /* Add space for ending '\0' */
Packit Service 31306d
    max_len++;
Packit Service 31306d
Packit Service 31306d
    tok = ssh_tokenize(list, ',');
Packit Service 31306d
    if ((tok == NULL) || (tok->tokens == NULL) || (tok->tokens[0] == NULL)) {
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    should_copy = calloc(1, max_len);
Packit Service 31306d
    if (should_copy == NULL) {
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    if (strlen(tok->tokens[0]) > 0) {
Packit Service 31306d
        should_copy[0] = true;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    for (i = 1; tok->tokens[i]; i++) {
Packit Service 31306d
        for (j = 0; j < i; j++) {
Packit Service 31306d
            if (strcmp(tok->tokens[i], tok->tokens[j]) == 0) {
Packit Service 31306d
                /* Found a duplicate; do not copy */
Packit Service 31306d
                should_copy[i] = false;
Packit Service 31306d
                break;
Packit Service 31306d
            }
Packit Service 31306d
        }
Packit Service 31306d
Packit Service 31306d
        /* No matching token before */
Packit Service 31306d
        if (j == i) {
Packit Service 31306d
            /* Only copy if it is not an empty string */
Packit Service 31306d
            if (strlen(tok->tokens[i]) > 0) {
Packit Service 31306d
                should_copy[i] = true;
Packit Service 31306d
            } else {
Packit Service 31306d
                should_copy[i] = false;
Packit Service 31306d
            }
Packit Service 31306d
        }
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    num_tokens = i;
Packit Service 31306d
Packit Service 31306d
    ret = calloc(1, max_len);
Packit Service 31306d
    if (ret == NULL) {
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    for (i = 0; i < num_tokens; i++) {
Packit Service 31306d
        if (should_copy[i]) {
Packit Service 31306d
            if (need_comma) {
Packit Service 31306d
                strncat(ret, ",", (max_len - strlen(ret) - 1));
Packit Service 31306d
            }
Packit Service 31306d
            strncat(ret, tok->tokens[i], (max_len - strlen(ret) - 1));
Packit Service 31306d
            need_comma = true;
Packit Service 31306d
        }
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    /* If no comma is needed, nothing was copied */
Packit Service 31306d
    if (!need_comma) {
Packit Service 31306d
        SAFE_FREE(ret);
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
out:
Packit Service 31306d
    SAFE_FREE(should_copy);
Packit Service 31306d
    ssh_tokens_free(tok);
Packit Service 31306d
    return ret;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
/**
Packit Service 31306d
 * @internal
Packit Service 31306d
 *
Packit Service 31306d
 * @brief Given two strings containing lists of tokens, return a newly
Packit Service 31306d
 * allocated string containing all the elements of the first list appended with
Packit Service 31306d
 * all the elements of the second list, without duplicates. The order of the
Packit Service 31306d
 * elements will be preserved.
Packit Service 31306d
 *
Packit Service 31306d
 * @param[in] list             The first list
Packit Service 31306d
 * @param[in] appended_list    The list to be appended
Packit Service 31306d
 *
Packit Service 31306d
 * @return  A newly allocated copy list containing all the elements of the
Packit Service 31306d
 * kept_list appended with the elements of the appended_list without duplicates;
Packit Service 31306d
 * NULL in case of error.
Packit Service 31306d
 */
Packit Service 31306d
char *ssh_append_without_duplicates(const char *list,
Packit Service 31306d
                                    const char *appended_list)
Packit Service 31306d
{
Packit Service 31306d
    size_t concat_len = 0;
Packit Service 31306d
    char *ret = NULL, *concat = NULL;
Packit Service 31306d
Packit Service 31306d
    if (list != NULL) {
Packit Service 31306d
        concat_len = strlen(list);
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    if (appended_list != NULL) {
Packit Service 31306d
        concat_len += strlen(appended_list);
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    if (concat_len == 0) {
Packit Service 31306d
        return NULL;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    /* Add room for ending '\0' and for middle ',' */
Packit Service 31306d
    concat_len += 2;
Packit Service 31306d
    concat = calloc(1, concat_len);
Packit Service 31306d
    if (concat == NULL) {
Packit Service 31306d
        return NULL;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    if (list != NULL) {
Packit Service 31306d
        strcpy(concat, list);
Packit Service 31306d
        strncat(concat, ",", concat_len - strlen(concat) - 1);
Packit Service 31306d
    }
Packit Service 31306d
    if (appended_list != NULL) {
Packit Service 31306d
        strncat(concat, appended_list, concat_len - strlen(concat) - 1);
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    ret = ssh_remove_duplicates(concat);
Packit Service 31306d
Packit Service 31306d
    SAFE_FREE(concat);
Packit Service 31306d
Packit Service 31306d
    return ret;
Packit Service 31306d
}