aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAris Adamantiadis <aris@0xbadc0de.be>2014-02-04 22:28:30 +0100
committerAris Adamantiadis <aris@0xbadc0de.be>2014-02-05 08:08:31 +0100
commit56f86cd4a1d774353b2095aebfdbdd73fa4276ba (patch)
tree1eed975397ea46bc0b0c8e72dccbc07551eaa98f
parentf265afacfbe6eab9db6a8aab39d2e0fbd2d1a1d7 (diff)
downloadlibssh-56f86cd4a1d774353b2095aebfdbdd73fa4276ba.tar.gz
libssh-56f86cd4a1d774353b2095aebfdbdd73fa4276ba.tar.xz
libssh-56f86cd4a1d774353b2095aebfdbdd73fa4276ba.zip
knownhosts: detect variations of ecdsa
-rw-r--r--include/libssh/knownhosts.h27
-rw-r--r--include/libssh/libssh.h1
-rw-r--r--src/kex.c72
-rw-r--r--src/known_hosts.c40
-rw-r--r--tests/client/torture_knownhosts.c20
5 files changed, 117 insertions, 43 deletions
diff --git a/include/libssh/knownhosts.h b/include/libssh/knownhosts.h
new file mode 100644
index 00000000..723c7ebc
--- /dev/null
+++ b/include/libssh/knownhosts.h
@@ -0,0 +1,27 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 20014 by Aris Adamantiadis <aris@badcode.be>
+ *
+ * This 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.
+ *
+ * This 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#ifndef KNOWNHOSTS_H_
+#define KNOWNHOSTS_H_
+
+char **ssh_knownhosts_algorithms(ssh_session session);
+
+#endif /* KNOWNHOSTS_H_ */
diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
index 7cb7cf36..b3f6ae4b 100644
--- a/include/libssh/libssh.h
+++ b/include/libssh/libssh.h
@@ -503,7 +503,6 @@ LIBSSH_API int ssh_key_cmp(const ssh_key k1,
const ssh_key k2,
enum ssh_keycmp_e what);
-LIBSSH_API int ssh_knownhosts_algorithms(ssh_session session);
LIBSSH_API int ssh_pki_generate(enum ssh_keytypes_e type, int parameter,
ssh_key *pkey);
LIBSSH_API int ssh_pki_import_privkey_base64(const char *b64_key,
diff --git a/src/kex.c b/src/kex.c
index 670ac515..8bf66231 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -35,6 +35,7 @@
#include "libssh/ssh2.h"
#include "libssh/string.h"
#include "libssh/curve25519.h"
+#include "libssh/knownhosts.h"
#ifdef HAVE_LIBGCRYPT
# define BLOWFISH "blowfish-cbc,"
@@ -374,16 +375,58 @@ void ssh_list_kex(struct ssh_kex_struct *kex) {
}
/**
+ * @internal
+ * @brief selects the hostkey mechanisms to be chosen for the key exchange,
+ * as some hostkey mechanisms may be present in known_hosts file and preferred
+ * @returns a cstring containing a comma-separated list of hostkey methods.
+ * NULL if no method matches
+ */
+static char *ssh_client_select_hostkeys(ssh_session session){
+ char methods_buffer[128]={0};
+ static const char *preferred_hostkeys[]={"ecdsa-sha2-nistp521","ecdsa-sha2-nistp384",
+ "ecdsa-sha2-nistp256", "ssh-rsa", "ssh-dss", "ssh-rsa1", NULL};
+ char **methods;
+ int i,j;
+ int needcoma=0;
+
+ methods = ssh_knownhosts_algorithms(session);
+ if (methods == NULL || methods[0] == NULL)
+ return NULL;
+
+ for (i=0;preferred_hostkeys[i] != NULL; ++i){
+ for (j=0; methods[j] != NULL; ++j){
+ if(strcmp(preferred_hostkeys[i], methods[j]) == 0){
+ if (verify_existing_algo(SSH_HOSTKEYS, methods[j])){
+ if(needcoma)
+ strncat(methods_buffer,",",sizeof(methods_buffer)-strlen(methods_buffer)-1);
+ strncat(methods_buffer, methods[j], sizeof(methods_buffer)-strlen(methods_buffer)-1);
+ needcoma = 1;
+ }
+ }
+ }
+ }
+ for(i=0;methods[i]!= NULL; ++i){
+ SAFE_FREE(methods[i]);
+ }
+ SAFE_FREE(methods);
+
+ if(strlen(methods_buffer) > 0){
+ SSH_LOG(SSH_LOG_DEBUG, "Changing host key method to \"%s\"", methods_buffer);
+ return strdup(methods_buffer);
+ } else {
+ SSH_LOG(SSH_LOG_DEBUG, "No supported kex method for existing key in known_hosts file");
+ return NULL;
+ }
+
+}
+/**
* @brief sets the key exchange parameters to be sent to the server,
* in function of the options and available methods.
*/
int set_client_kex(ssh_session session){
struct ssh_kex_struct *client= &session->next_crypto->client_kex;
const char *wanted;
- char methods_buffer[128]={0};
- int prefered_hostkeys[]={SSH_KEYTYPE_ECDSA, SSH_KEYTYPE_RSA,
- SSH_KEYTYPE_DSS, SSH_KEYTYPE_RSA1, SSH_KEYTYPE_UNKNOWN};
- int i, methods, needcoma=0;
+ int i;
ssh_get_random(client->cookie, 16, 0);
@@ -391,25 +434,8 @@ int set_client_kex(ssh_session session){
/* first check if we have specific host key methods */
if(session->opts.wanted_methods[SSH_HOSTKEYS] == NULL){
/* Only if no override */
- methods = ssh_knownhosts_algorithms(session);
- if (methods != SSH_ERROR && methods != 0){
- for(i=0; prefered_hostkeys[i] != SSH_KEYTYPE_UNKNOWN;++i){
- if (methods & (1 << prefered_hostkeys[i])){
- if (verify_existing_algo(SSH_HOSTKEYS, ssh_key_type_to_char(prefered_hostkeys[i]))){
- if(needcoma)
- strncat(methods_buffer,",",sizeof(methods_buffer)-strlen(methods_buffer)-1);
- strncat(methods_buffer, ssh_key_type_to_char(prefered_hostkeys[i]), sizeof(methods_buffer)-strlen(methods_buffer)-1);
- needcoma = 1;
- }
- }
- }
- if(strlen(methods_buffer) > 0){
- SSH_LOG(SSH_LOG_DEBUG, "Changing host key method to \"%s\"", methods_buffer);
- session->opts.wanted_methods[SSH_HOSTKEYS] = strdup(methods_buffer);
- } else {
- SSH_LOG(SSH_LOG_DEBUG, "No supported kex method for existing key in known_hosts file");
- }
- }
+ session->opts.wanted_methods[SSH_HOSTKEYS] =
+ ssh_client_select_hostkeys(session);
}
for (i = 0; i < KEX_METHODS_SIZE; i++) {
diff --git a/src/known_hosts.c b/src/known_hosts.c
index f2b2fde8..21f6cf29 100644
--- a/src/known_hosts.c
+++ b/src/known_hosts.c
@@ -34,7 +34,7 @@
#include "libssh/misc.h"
#include "libssh/pki.h"
#include "libssh/options.h"
-
+#include "libssh/knownhosts.h"
/*todo: remove this include */
#include "libssh/string.h"
@@ -647,29 +647,33 @@ int ssh_write_knownhost(ssh_session session) {
return 0;
}
+#define KNOWNHOSTS_MAXTYPES 10
+
/**
+ * @internal
* @brief Check which kind of host keys should be preferred for connection
* by reading the known_hosts file.
*
* @param[in] session The SSH session to use.
*
- * @returns Bitfield of supported SSH hostkey algorithms
- * SSH_ERROR on error
+ * @returns array of supported key types
+ * NULL on error
*/
-int ssh_knownhosts_algorithms(ssh_session session) {
+char **ssh_knownhosts_algorithms(ssh_session session) {
FILE *file = NULL;
char **tokens;
char *host;
char *hostport;
const char *type;
int match;
- int ret = 0;
+ char **array;
+ int i=0, j;
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_ERROR;
+ return NULL;
}
}
@@ -683,8 +687,13 @@ int ssh_knownhosts_algorithms(ssh_session session) {
ssh_set_error_oom(session);
SAFE_FREE(host);
SAFE_FREE(hostport);
+ return NULL;
+ }
- return SSH_ERROR;
+ array = malloc(sizeof(char *) * KNOWNHOSTS_MAXTYPES);
+ if (array==NULL){
+ ssh_set_error_oom(session);
+ return NULL;
}
do {
@@ -709,11 +718,24 @@ int ssh_knownhosts_algorithms(ssh_session session) {
/* We got a match. Now check the key type */
SSH_LOG(SSH_LOG_DEBUG, "server %s:%d has %s in known_hosts",
host, session->opts.port, type);
- ret |= 1 << ssh_key_type_from_name(type);
+ /* don't copy more than once */
+ for(j=0;j<i && match;++j){
+ if(strcmp(array[j], type)==0)
+ match=0;
+ }
+ if (match){
+ array[i] = strdup(type);
+ i++;
+ if(i>= KNOWNHOSTS_MAXTYPES-1){
+ tokens_free(tokens);
+ break;
+ }
+ }
}
tokens_free(tokens);
} while (1);
+ array[i]=NULL;
SAFE_FREE(host);
SAFE_FREE(hostport);
if (file != NULL) {
@@ -721,7 +743,7 @@ int ssh_knownhosts_algorithms(ssh_session session) {
}
/* Return the current state at end of file */
- return ret;
+ return array;
}
/** @} */
diff --git a/tests/client/torture_knownhosts.c b/tests/client/torture_knownhosts.c
index 5abc2fc4..70f21b19 100644
--- a/tests/client/torture_knownhosts.c
+++ b/tests/client/torture_knownhosts.c
@@ -23,6 +23,7 @@
#include "torture.h"
#include "session.c"
+#include "known_hosts.c"
#define KNOWNHOSTFILES "libssh_torture_knownhosts"
#define BADRSA "AAAAB3NzaC1yc2EAAAADAQABAAABAQChm5" \
@@ -255,8 +256,7 @@ static void torture_knownhosts_precheck(void **state) {
ssh_session session = *state;
FILE *file;
int rc;
- int dsa;
- int rsa;
+ char **kex;
rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost");
assert_true(rc == SSH_OK);
@@ -270,14 +270,14 @@ static void torture_knownhosts_precheck(void **state) {
fprintf(file, "localhost ssh-dss %s\n", BADDSA);
fclose(file);
- rc = ssh_knownhosts_algorithms(session);
- assert_true(rc != SSH_ERROR);
- dsa = 1 << SSH_KEYTYPE_DSS;
- rsa = 1 << SSH_KEYTYPE_RSA;
- assert_true(rc & dsa);
- assert_true(rc & rsa);
- /* nothing else than dsa and rsa */
- assert_true((rc & (dsa | rsa)) == rc);
+ kex = ssh_knownhosts_algorithms(session);
+ assert_true(kex != NULL);
+ assert_string_equal(kex[0],"ssh-rsa");
+ assert_string_equal(kex[1],"ssh-dss");
+ assert_true(kex[2]==NULL);
+ free(kex[1]);
+ free(kex[0]);
+ free(kex);
}
int torture_run_tests(void) {