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 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) const char *ssh_kex_get_default_methods(uint32_t algo)
{ {
if (algo >= KEX_METHODS_SIZE) { 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, static int cmp_first_kex_algo(const char *client_str,
const char *server_str) { const char *server_str) {
size_t client_kex_len;
size_t server_kex_len;
char *colon;
int is_wrong = 1; int is_wrong = 1;
char **server_str_tokens = NULL;
char **client_str_tokens = NULL;
if ((client_str == NULL) || (server_str == NULL)) { colon = strchr(client_str, ',');
goto out; if (colon == NULL) {
client_kex_len = strlen(client_str);
} else {
client_kex_len = colon - client_str;
} }
client_str_tokens = tokenize(client_str); colon = strchr(server_str, ',');
if (colon == NULL) {
if (client_str_tokens == NULL) { server_kex_len = strlen(server_str);
goto out; } else {
server_kex_len = colon - server_str;
} }
if (client_str_tokens[0] == NULL) { if (client_kex_len != server_kex_len) {
goto freeout; return is_wrong;
} }
server_str_tokens = tokenize(server_str); is_wrong = (strncmp(client_str, server_str, client_kex_len) != 0);
if (server_str_tokens == NULL) {
goto freeout;
}
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; return is_wrong;
} }

View File

@@ -38,6 +38,7 @@
#include "libssh/knownhosts.h" #include "libssh/knownhosts.h"
/*todo: remove this include */ /*todo: remove this include */
#include "libssh/string.h" #include "libssh/string.h"
#include "libssh/token.h"
#ifndef _WIN32 #ifndef _WIN32
# include <netinet/in.h> # 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 * @internal
* *
@@ -86,58 +70,63 @@ static void tokens_free(char **tokens) {
* free that value. NULL if no match was found or the file * free that value. NULL if no match was found or the file
* was not found. * was not found.
*/ */
static char **ssh_get_knownhost_line(FILE **file, const char *filename, static struct ssh_tokens_st *ssh_get_knownhost_line(FILE **file,
const char **found_type) { const char *filename,
char buffer[4096] = {0}; const char **found_type)
char *ptr; {
char **tokens; char buffer[4096] = {0};
char *ptr;
struct ssh_tokens_st *tokens;
if(*file == NULL){
*file = fopen(filename,"r");
if (*file == NULL) { if (*file == NULL) {
return NULL; *file = fopen(filename,"r");
} if (*file == NULL) {
} return NULL;
}
while (fgets(buffer, sizeof(buffer), *file)) {
ptr = strchr(buffer, '\n');
if (ptr) {
*ptr = '\0';
} }
ptr = strchr(buffer,'\r'); while (fgets(buffer, sizeof(buffer), *file)) {
if (ptr) { ptr = strchr(buffer, '\n');
*ptr = '\0'; 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] == '#') { fclose(*file);
continue; /* skip empty lines */ *file = NULL;
}
tokens = ssh_space_tokenize(buffer); /* we did not find anything, end of file*/
if (tokens == NULL) { return 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;
} }
/** /**
@@ -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() * @deprecated Please use ssh_session_is_known_server()
* @see ssh_session_is_known_server() * @see ssh_session_is_known_server()
*/ */
int ssh_is_server_known(ssh_session session) { int ssh_is_server_known(ssh_session session)
FILE *file = NULL; {
char **tokens; FILE *file = NULL;
char *host; char *host;
char *hostport; char *hostport;
const char *type; const char *type;
int match; int match;
int i=0; int i = 0;
char * files[3]; char *files[3];
int ret = SSH_SERVER_NOT_KNOWN;
if (session->opts.knownhosts == NULL) { struct ssh_tokens_st *tokens;
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; 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) { if (session->opts.host == NULL) {
ssh_set_error(session, SSH_FATAL, ssh_set_error(session, SSH_FATAL,
"Can't verify host in known hosts if the hostname isn't known"); "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){ if (session->current_crypto == NULL){
ssh_set_error(session, SSH_FATAL, ssh_set_error(session, SSH_FATAL,
"ssh_is_host_known called without cryptographic context"); "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(host);
SAFE_FREE(hostport); SAFE_FREE(hostport);
if (file != NULL) {
return SSH_SERVER_ERROR; fclose(file);
}
/* 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;
} }
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 */ /* Return the current state at end of file */
if (strcmp(pubkey_type, type) != 0) { return ret;
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;
} }
/** /**