1
0
mirror of https://git.libssh.org/projects/libssh.git synced 2025-09-11 13:30:43 +03:00

token, kex: Add functions to handle tokens lists

The added functions allow splitting chains of tokens separated by a
given character (usually ','), and extracting matching parts between two
chains of tokens.

The previously existing functions in kex.c were replaced by the
introduced ones.

Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
This commit is contained in:
Anderson Toshiyuki Sasaki
2019-05-15 17:48:41 +02:00
committed by Andreas Schneider
parent b0ff64bf1b
commit 2c4850cbbd
6 changed files with 472 additions and 111 deletions

44
include/libssh/token.h Normal file
View File

@@ -0,0 +1,44 @@
/*
* token.h - Tokens list handling
*
* This file is part of the SSH Library
*
* Copyright (c) 2019 by Red Hat, Inc.
*
* Author: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#ifndef TOKEN_H_
#define TOKEN_H_
struct ssh_tokens_st {
char *buffer;
char **tokens;
};
struct ssh_tokens_st *ssh_tokenize(const char *chain, char separator);
void ssh_tokens_free(struct ssh_tokens_st *tokens);
char *ssh_find_matching(const char *available_d,
const char *preferred_d);
char *ssh_find_all_matching(const char *available_d,
const char *preferred_d);
#endif /* TOKEN_H_ */

View File

@@ -164,6 +164,7 @@ set(libssh_SRCS
external/sc25519.c
chachapoly.c
config_parser.c
token.c
)
if (DEFAULT_C_NO_DEPRECATION_FLAGS)

112
src/kex.c
View File

@@ -43,6 +43,7 @@
#include "libssh/misc.h"
#include "libssh/pki.h"
#include "libssh/bignum.h"
#include "libssh/token.h"
#ifdef WITH_BLOWFISH_CIPHER
# if defined(HAVE_OPENSSL_BLOWFISH_H) || defined(HAVE_LIBGCRYPT) || defined(HAVE_LIBMBEDCRYPTO)
@@ -296,117 +297,6 @@ const char *ssh_kex_get_description(uint32_t algo) {
return ssh_kex_descriptions[algo];
}
/* find_matching gets 2 parameters : a list of available objects (available_d), separated by colons,*/
/* and a list of preferred objects (preferred_d) */
/* it will return a strduped pointer on the first preferred object found in the available objects list */
char *ssh_find_matching(const char *available_d, const char *preferred_d){
char ** tok_available, **tok_preferred;
int i_avail, i_pref;
char *ret;
if ((available_d == NULL) || (preferred_d == NULL)) {
return NULL; /* don't deal with null args */
}
tok_available = tokenize(available_d);
if (tok_available == NULL) {
return NULL;
}
tok_preferred = tokenize(preferred_d);
if (tok_preferred == NULL) {
SAFE_FREE(tok_available[0]);
SAFE_FREE(tok_available);
return NULL;
}
for(i_pref=0; tok_preferred[i_pref] ; ++i_pref){
for(i_avail=0; tok_available[i_avail]; ++i_avail){
if(strcmp(tok_available[i_avail],tok_preferred[i_pref]) == 0){
/* match */
ret=strdup(tok_available[i_avail]);
/* free the tokens */
SAFE_FREE(tok_available[0]);
SAFE_FREE(tok_preferred[0]);
SAFE_FREE(tok_available);
SAFE_FREE(tok_preferred);
return ret;
}
}
}
SAFE_FREE(tok_available[0]);
SAFE_FREE(tok_preferred[0]);
SAFE_FREE(tok_available);
SAFE_FREE(tok_preferred);
return NULL;
}
static char *ssh_find_all_matching(const char *available_d,
const char *preferred_d)
{
char **tok_available, **tok_preferred;
int i_avail, i_pref;
char *ret;
unsigned max, len, pos = 0;
if ((available_d == NULL) || (preferred_d == NULL)) {
return NULL; /* don't deal with null args */
}
max = MAX(strlen(available_d), strlen(preferred_d));
ret = malloc(max+1);
if (ret == NULL) {
return NULL;
}
ret[0] = 0;
tok_available = tokenize(available_d);
if (tok_available == NULL) {
SAFE_FREE(ret);
return NULL;
}
tok_preferred = tokenize(preferred_d);
if (tok_preferred == NULL) {
SAFE_FREE(ret);
SAFE_FREE(tok_available[0]);
SAFE_FREE(tok_available);
return NULL;
}
for (i_pref = 0; tok_preferred[i_pref] ; ++i_pref) {
for (i_avail = 0; tok_available[i_avail]; ++i_avail) {
int cmp = strcmp(tok_available[i_avail],tok_preferred[i_pref]);
if (cmp == 0) {
/* match */
if (pos != 0) {
ret[pos] = ',';
pos++;
}
len = strlen(tok_available[i_avail]);
memcpy(&ret[pos], tok_available[i_avail], len);
pos += len;
ret[pos] = '\0';
}
}
}
if (ret[0] == '\0') {
SAFE_FREE(ret);
ret = NULL;
}
SAFE_FREE(tok_available[0]);
SAFE_FREE(tok_preferred[0]);
SAFE_FREE(tok_available);
SAFE_FREE(tok_preferred);
return ret;
}
/**
* @internal
* @brief returns whether the first client key exchange algorithm or

262
src/token.c Normal file
View File

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

View File

@@ -20,6 +20,7 @@ set(LIBSSH_UNIT_TESTS
torture_temp_file
torture_push_pop_dir
torture_session_keys
torture_tokens
)
set(LIBSSH_THREAD_UNIT_TESTS

View File

@@ -0,0 +1,163 @@
/*
* torture_tokens.c - Tests for tokens list handling
*
* This file is part of the SSH Library
*
* Copyright (c) 2019 by Red Hat, Inc.
*
* Author: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#define LIBSSH_STATIC
#include "torture.h"
#include "libssh/token.h"
#include "libssh/priv.h"
static void torture_find_matching(UNUSED_PARAM(void **state))
{
char *matching;
/* Match with single token */
matching = ssh_find_matching("a,b,c", "b");
assert_non_null(matching);
assert_string_equal(matching, "b");
SAFE_FREE(matching);
/* Match sequence, get first preferred */
matching = ssh_find_matching("a,b,c", "b,c");
assert_non_null(matching);
assert_string_equal(matching, "b");
SAFE_FREE(matching);
/* Only one token allowed */
matching = ssh_find_matching("c", "a,b,c");
assert_non_null(matching);
assert_string_equal(matching, "c");
SAFE_FREE(matching);
/* Different order in allowed and preferred; gets preferred */
matching = ssh_find_matching("c,b,a", "a,b,c");
assert_non_null(matching);
assert_string_equal(matching, "a");
SAFE_FREE(matching);
/* No matching returns NULL */
matching = ssh_find_matching("c,b,a", "d,e,f");
assert_null(matching);
}
static void torture_find_all_matching(UNUSED_PARAM(void **state))
{
char *matching;
/* Match with single token */
matching = ssh_find_all_matching("a,b,c", "b");
assert_non_null(matching);
assert_string_equal(matching, "b");
SAFE_FREE(matching);
/* Match sequence, get first preferred */
matching = ssh_find_all_matching("a,b,c", "b,c");
assert_non_null(matching);
assert_string_equal(matching, "b,c");
SAFE_FREE(matching);
/* Only one token allowed */
matching = ssh_find_all_matching("c", "a,b,c");
assert_non_null(matching);
assert_string_equal(matching, "c");
SAFE_FREE(matching);
/* Different order in allowed and preferred; gets preferred */
matching = ssh_find_all_matching("c,b,a", "a,c,b");
assert_non_null(matching);
assert_string_equal(matching, "a,c,b");
SAFE_FREE(matching);
/* No matching returns NULL */
matching = ssh_find_all_matching("c,b,a", "d,e,f");
assert_null(matching);
}
static void tokenize_compare_expected(const char *chain, const char **expected,
size_t num_expected)
{
struct ssh_tokens_st *tokens;
size_t i;
tokens = ssh_tokenize(chain, ',');
assert_non_null(tokens);
if (expected != NULL) {
assert_non_null(tokens->tokens);
for (i = 0; i < num_expected; i++) {
assert_non_null(tokens->tokens[i]);
assert_non_null(expected[i]);
assert_string_equal(tokens->tokens[i], expected[i]);
}
assert_null(tokens->tokens[i]);
i = 0;
printf("Tokenizing \"%s\" resulted in: ", chain);
while (tokens->tokens[i]) {
printf("\"%s\" ", tokens->tokens[i++]);
}
printf("\n");
}
ssh_tokens_free(tokens);
}
static void torture_tokens_sanity(UNUSED_PARAM(void **state))
{
const char *simple[] = {"a", "b", "c"};
const char *colon_first[] = {"", "a", "b", "c"};
const char *colon_end[] = {"a", "b", "c"};
const char *colon_both[] = {"", "a", "b", "c"};
const char *single[] = {"abc"};
const char *empty[] = {""};
const char *single_colon[] = {""};
tokenize_compare_expected("a,b,c", simple, 3);
tokenize_compare_expected(",a,b,c", colon_first, 4);
tokenize_compare_expected("a,b,c,", colon_end, 3);
tokenize_compare_expected(",a,b,c,", colon_both, 4);
tokenize_compare_expected("abc", single, 1);
tokenize_compare_expected("", empty, 1);
tokenize_compare_expected(",", single_colon, 1);
}
int torture_run_tests(void)
{
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test(torture_tokens_sanity),
cmocka_unit_test(torture_find_matching),
cmocka_unit_test(torture_find_all_matching),
};
ssh_init();
torture_filter_tests(tests);
rc = cmocka_run_group_tests(tests, NULL, NULL);
ssh_finalize();
return rc;
}