diff --git a/src/kex.c b/src/kex.c index fbb4b8a4..750bc466 100644 --- a/src/kex.c +++ b/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= 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; } diff --git a/src/known_hosts.c b/src/known_hosts.c index 093188ec..80520907 100644 --- a/src/known_hosts.c +++ b/src/known_hosts.c @@ -38,6 +38,7 @@ #include "libssh/knownhosts.h" /*todo: remove this include */ #include "libssh/string.h" +#include "libssh/token.h" #ifndef _WIN32 # include @@ -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; } /**