mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-04 20:30:38 +09:00
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:
committed by
Andreas Schneider
parent
2c4850cbbd
commit
bc95a51710
126
src/kex.c
126
src/kex.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user