kex, known_hosts: Use new tokens functions

Replace the old tokens handling functions usage with the new implementation.

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 18:57:14 +02:00
committed by Andreas Schneider
parent 2c4850cbbd
commit bc95a51710
2 changed files with 191 additions and 286 deletions

126
src/kex.c
View File

@@ -186,92 +186,6 @@ static const char *ssh_kex_descriptions[] = {
NULL
};
/* tokenize will return a token of strings delimited by ",". the first element has to be freed */
static char **tokenize(const char *chain){
char **tokens;
size_t n=1;
size_t i=0;
char *tmp;
char *ptr;
tmp = strdup(chain);
if (tmp == NULL) {
return NULL;
}
ptr = tmp;
while(*ptr){
if(*ptr==','){
n++;
*ptr=0;
}
ptr++;
}
/* now n contains the number of tokens, the first possibly empty if the list was empty too e.g. "" */
tokens = calloc(n + 1, sizeof(char *)); /* +1 for the null */
if (tokens == NULL) {
SAFE_FREE(tmp);
return NULL;
}
ptr=tmp;
for(i=0;i<n;i++){
tokens[i]=ptr;
while(*ptr)
ptr++; // find a zero
ptr++; // then go one step further
}
tokens[i]=NULL;
return tokens;
}
/* same as tokenize(), but with spaces instead of ',' */
/* TODO FIXME rewrite me! */
char **ssh_space_tokenize(const char *chain){
char **tokens;
size_t n=1;
size_t i=0;
char *tmp;
char *ptr;
tmp = strdup(chain);
if (tmp == NULL) {
return NULL;
}
ptr = tmp;
while(*ptr==' ')
++ptr; /* skip initial spaces */
while(*ptr){
if(*ptr==' '){
n++; /* count one token per word */
*ptr=0;
while(*(ptr+1)==' '){ /* don't count if the tokens have more than 2 spaces */
*(ptr++)=0;
}
}
ptr++;
}
/* now n contains the number of tokens, the first possibly empty if the list was empty too e.g. "" */
tokens = calloc(n + 1, sizeof(char *)); /* +1 for the null */
if (tokens == NULL) {
SAFE_FREE(tmp);
return NULL;
}
ptr=tmp; /* we don't pass the initial spaces because the "tmp" pointer is needed by the caller */
/* function to free the tokens. */
for(i=0;i<n;i++){
tokens[i]=ptr;
if(i!=n-1){
while(*ptr)
ptr++; // find a zero
while(!*(ptr+1))
++ptr; /* if the zero is followed by other zeros, go through them */
ptr++; // then go one step further
}
}
tokens[i]=NULL;
return tokens;
}
const char *ssh_kex_get_default_methods(uint32_t algo)
{
if (algo >= KEX_METHODS_SIZE) {
@@ -306,37 +220,33 @@ const char *ssh_kex_get_description(uint32_t algo) {
*/
static int cmp_first_kex_algo(const char *client_str,
const char *server_str) {
size_t client_kex_len;
size_t server_kex_len;
char *colon;
int is_wrong = 1;
char **server_str_tokens = NULL;
char **client_str_tokens = NULL;
if ((client_str == NULL) || (server_str == NULL)) {
goto out;
colon = strchr(client_str, ',');
if (colon == NULL) {
client_kex_len = strlen(client_str);
} else {
client_kex_len = colon - client_str;
}
client_str_tokens = tokenize(client_str);
if (client_str_tokens == NULL) {
goto out;
colon = strchr(server_str, ',');
if (colon == NULL) {
server_kex_len = strlen(server_str);
} else {
server_kex_len = colon - server_str;
}
if (client_str_tokens[0] == NULL) {
goto freeout;
if (client_kex_len != server_kex_len) {
return is_wrong;
}
server_str_tokens = tokenize(server_str);
if (server_str_tokens == NULL) {
goto freeout;
}
is_wrong = (strncmp(client_str, server_str, client_kex_len) != 0);
is_wrong = (strcmp(client_str_tokens[0], server_str_tokens[0]) != 0);
SAFE_FREE(server_str_tokens[0]);
SAFE_FREE(server_str_tokens);
freeout:
SAFE_FREE(client_str_tokens[0]);
SAFE_FREE(client_str_tokens);
out:
return is_wrong;
}

View File

@@ -38,6 +38,7 @@
#include "libssh/knownhosts.h"
/*todo: remove this include */
#include "libssh/string.h"
#include "libssh/token.h"
#ifndef _WIN32
# include <netinet/in.h>
@@ -50,23 +51,6 @@
* @{
*/
/**
* @internal
*
* @brief Free a token array.
*/
static void tokens_free(char **tokens) {
if (tokens == NULL) {
return;
}
SAFE_FREE(tokens[0]);
/* It's not needed to free other pointers because tokens generated by
* space_tokenize fit all in one malloc
*/
SAFE_FREE(tokens);
}
/**
* @internal
*
@@ -86,58 +70,63 @@ static void tokens_free(char **tokens) {
* free that value. NULL if no match was found or the file
* was not found.
*/
static char **ssh_get_knownhost_line(FILE **file, const char *filename,
const char **found_type) {
char buffer[4096] = {0};
char *ptr;
char **tokens;
static struct ssh_tokens_st *ssh_get_knownhost_line(FILE **file,
const char *filename,
const char **found_type)
{
char buffer[4096] = {0};
char *ptr;
struct ssh_tokens_st *tokens;
if(*file == NULL){
*file = fopen(filename,"r");
if (*file == NULL) {
return NULL;
}
}
while (fgets(buffer, sizeof(buffer), *file)) {
ptr = strchr(buffer, '\n');
if (ptr) {
*ptr = '\0';
*file = fopen(filename,"r");
if (*file == NULL) {
return NULL;
}
}
ptr = strchr(buffer,'\r');
if (ptr) {
*ptr = '\0';
while (fgets(buffer, sizeof(buffer), *file)) {
ptr = strchr(buffer, '\n');
if (ptr) {
*ptr = '\0';
}
ptr = strchr(buffer,'\r');
if (ptr) {
*ptr = '\0';
}
if (buffer[0] == '\0' || buffer[0] == '#') {
continue; /* skip empty lines */
}
tokens = ssh_tokenize(buffer, ' ');
if (tokens == NULL) {
fclose(*file);
*file = NULL;
return NULL;
}
if (tokens->tokens[0] == NULL ||
tokens->tokens[1] == NULL ||
tokens->tokens[2] == NULL)
{
/* it should have at least 3 tokens */
ssh_tokens_free(tokens);
continue;
}
*found_type = tokens->tokens[1];
return tokens;
}
if (buffer[0] == '\0' || buffer[0] == '#') {
continue; /* skip empty lines */
}
fclose(*file);
*file = NULL;
tokens = ssh_space_tokenize(buffer);
if (tokens == NULL) {
fclose(*file);
*file = NULL;
return NULL;
}
if(tokens[0] == NULL || tokens[1] == NULL || tokens[2] == NULL) {
/* it should have at least 3 tokens */
tokens_free(tokens);
continue;
}
*found_type = tokens[1];
return tokens;
}
fclose(*file);
*file = NULL;
/* we did not find anything, end of file*/
return NULL;
/* we did not find anything, end of file*/
return NULL;
}
/**
@@ -291,137 +280,143 @@ static int match_hashed_host(const char *host, const char *sourcehash)
*/
/**
* @brief This function is depcrecated
* @brief This function is deprecated
*
* @deprecated Please use ssh_session_is_known_server()
* @see ssh_session_is_known_server()
*/
int ssh_is_server_known(ssh_session session) {
FILE *file = NULL;
char **tokens;
char *host;
char *hostport;
const char *type;
int match;
int i=0;
char * files[3];
int ret = SSH_SERVER_NOT_KNOWN;
int ssh_is_server_known(ssh_session session)
{
FILE *file = NULL;
char *host;
char *hostport;
const char *type;
int match;
int i = 0;
char *files[3];
if (session->opts.knownhosts == NULL) {
if (ssh_options_apply(session) < 0) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"Can't find a known_hosts file");
struct ssh_tokens_st *tokens;
return SSH_SERVER_FILE_NOT_FOUND;
int ret = SSH_SERVER_NOT_KNOWN;
if (session->opts.knownhosts == NULL) {
if (ssh_options_apply(session) < 0) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"Can't find a known_hosts file");
return SSH_SERVER_FILE_NOT_FOUND;
}
}
}
if (session->opts.host == NULL) {
ssh_set_error(session, SSH_FATAL,
"Can't verify host in known hosts if the hostname isn't known");
if (session->opts.host == NULL) {
ssh_set_error(session, SSH_FATAL,
"Can't verify host in known hosts if the hostname isn't known");
return SSH_SERVER_ERROR;
}
return SSH_SERVER_ERROR;
}
if (session->current_crypto == NULL){
ssh_set_error(session, SSH_FATAL,
"ssh_is_host_known called without cryptographic context");
if (session->current_crypto == NULL){
ssh_set_error(session, SSH_FATAL,
"ssh_is_host_known called without cryptographic context");
return SSH_SERVER_ERROR;
}
host = ssh_lowercase(session->opts.host);
hostport = ssh_hostport(host, session->opts.port > 0 ? session->opts.port : 22);
if (host == NULL || hostport == NULL) {
ssh_set_error_oom(session);
SAFE_FREE(host);
SAFE_FREE(hostport);
return SSH_SERVER_ERROR;
}
/* Set the list of known hosts files */
i = 0;
if (session->opts.global_knownhosts != NULL){
files[i++] = session->opts.global_knownhosts;
}
files[i++] = session->opts.knownhosts;
files[i] = NULL;
i = 0;
do {
tokens = ssh_get_knownhost_line(&file,
files[i],
&type);
/* End of file, return the current state or use next file */
if (tokens == NULL) {
++i;
if(files[i] == NULL)
break;
else
continue;
}
match = match_hashed_host(host, tokens->tokens[0]);
if (match == 0){
match = match_hostname(hostport, tokens->tokens[0],
strlen(tokens->tokens[0]));
}
if (match == 0) {
match = match_hostname(host, tokens->tokens[0],
strlen(tokens->tokens[0]));
}
if (match == 0) {
match = match_hashed_host(hostport, tokens->tokens[0]);
}
if (match) {
ssh_key pubkey = ssh_dh_get_current_server_publickey(session);
const char *pubkey_type = ssh_key_type_to_char(ssh_key_type(pubkey));
/* We got a match. Now check the key type */
if (strcmp(pubkey_type, type) != 0) {
SSH_LOG(SSH_LOG_PACKET,
"ssh_is_server_known: server type [%s] doesn't match the "
"type [%s] in known_hosts file",
pubkey_type,
type);
/* Different type. We don't override the known_changed error which is
* more important */
if (ret != SSH_SERVER_KNOWN_CHANGED)
ret = SSH_SERVER_FOUND_OTHER;
ssh_tokens_free(tokens);
continue;
}
/* so we know the key type is good. We may get a good key or a bad key. */
match = check_public_key(session, tokens->tokens);
ssh_tokens_free(tokens);
if (match < 0) {
ret = SSH_SERVER_ERROR;
break;
} else if (match == 1) {
ret = SSH_SERVER_KNOWN_OK;
break;
} else if(match == 0) {
/* We override the status with the wrong key state */
ret = SSH_SERVER_KNOWN_CHANGED;
}
} else {
ssh_tokens_free(tokens);
}
} while (1);
if ((ret == SSH_SERVER_NOT_KNOWN) &&
(session->opts.StrictHostKeyChecking == 0)) {
ssh_write_knownhost(session);
ret = SSH_SERVER_KNOWN_OK;
}
return SSH_SERVER_ERROR;
}
host = ssh_lowercase(session->opts.host);
hostport = ssh_hostport(host, session->opts.port > 0 ? session->opts.port : 22);
if (host == NULL || hostport == NULL) {
ssh_set_error_oom(session);
SAFE_FREE(host);
SAFE_FREE(hostport);
return SSH_SERVER_ERROR;
}
/* set the list of known hosts */
i = 0;
if (session->opts.global_knownhosts != NULL){
files[i++]=session->opts.global_knownhosts;
}
files[i++] = session->opts.knownhosts;
files[i] = NULL;
i = 0;
do {
tokens = ssh_get_knownhost_line(&file,
files[i],
&type);
/* End of file, return the current state or use next file */
if (tokens == NULL) {
++i;
if(files[i] == NULL)
break;
else
continue;
if (file != NULL) {
fclose(file);
}
match = match_hashed_host(host, tokens[0]);
if (match == 0){
match = match_hostname(hostport, tokens[0], strlen(tokens[0]));
}
if (match == 0) {
match = match_hostname(host, tokens[0], strlen(tokens[0]));
}
if (match == 0) {
match = match_hashed_host(hostport, tokens[0]);
}
if (match) {
ssh_key pubkey = ssh_dh_get_current_server_publickey(session);
const char *pubkey_type = ssh_key_type_to_char(ssh_key_type(pubkey));
/* We got a match. Now check the key type */
if (strcmp(pubkey_type, type) != 0) {
SSH_LOG(SSH_LOG_PACKET,
"ssh_is_server_known: server type [%s] doesn't match the "
"type [%s] in known_hosts file",
pubkey_type,
type);
/* Different type. We don't override the known_changed error which is
* more important */
if (ret != SSH_SERVER_KNOWN_CHANGED)
ret = SSH_SERVER_FOUND_OTHER;
tokens_free(tokens);
continue;
}
/* so we know the key type is good. We may get a good key or a bad key. */
match = check_public_key(session, tokens);
tokens_free(tokens);
if (match < 0) {
ret = SSH_SERVER_ERROR;
break;
} else if (match == 1) {
ret = SSH_SERVER_KNOWN_OK;
break;
} else if(match == 0) {
/* We override the status with the wrong key state */
ret = SSH_SERVER_KNOWN_CHANGED;
}
} else {
tokens_free(tokens);
}
} while (1);
if ((ret == SSH_SERVER_NOT_KNOWN) &&
(session->opts.StrictHostKeyChecking == 0)) {
ssh_write_knownhost(session);
ret = SSH_SERVER_KNOWN_OK;
}
SAFE_FREE(host);
SAFE_FREE(hostport);
if (file != NULL) {
fclose(file);
}
/* Return the current state at end of file */
return ret;
/* Return the current state at end of file */
return ret;
}
/**