New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
libssh2: add support for forcing a specific host key #4747
Changes from 1 commit
f5515dc
cd33780
9827349
a70f821
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -106,6 +106,7 @@ static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc); | |
static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc); | ||
static LIBSSH2_FREE_FUNC(my_libssh2_free); | ||
|
||
static CURLcode ssh_force_knownhost_key_type(struct connectdata *conn); | ||
static CURLcode ssh_connect(struct connectdata *conn, bool *done); | ||
static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done); | ||
static CURLcode ssh_do(struct connectdata *conn, bool *done); | ||
|
@@ -648,6 +649,105 @@ static CURLcode ssh_check_fingerprint(struct connectdata *conn) | |
return ssh_knownhost(conn); | ||
} | ||
|
||
/* | ||
* ssh_force_knownhost_key_type() will check the known hosts file and try to | ||
* force a specific public key type from the server if an entry is found. | ||
*/ | ||
static CURLcode ssh_force_knownhost_key_type(struct connectdata *conn) | ||
{ | ||
#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519 | ||
static const char * const hostkey_method_ssh_ed25519 | ||
= "ssh-ed25519"; | ||
#endif | ||
#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521 | ||
static const char * const hostkey_method_ssh_ecdsa_521 | ||
= "ecdsa-sha2-nistp521"; | ||
#endif | ||
#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384 | ||
static const char * const hostkey_method_ssh_ecdsa_384 | ||
= "ecdsa-sha2-nistp284"; | ||
#endif | ||
#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256 | ||
static const char * const hostkey_method_ssh_ecdsa_256 | ||
= "ecdsa-sha2-nistp256"; | ||
#endif | ||
static const char * const hostkey_method_ssh_rsa | ||
= "ssh-rsa"; | ||
static const char * const hostkey_method_ssh_dss | ||
= "ssh-dss"; | ||
|
||
CURLcode result = CURLE_OK; | ||
const char *hostkey_method = NULL; | ||
struct ssh_conn *sshc = &conn->proto.sshc; | ||
struct Curl_easy *data = conn->data; | ||
struct libssh2_knownhost* store = NULL; | ||
bool found = false; | ||
|
||
/*&& data->set.str[STRING_SSH_KNOWNHOSTS] | ||
*<-- unnecessary because of sshc->kh? */ | ||
if(sshc->kh && !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) { | ||
/* lets try to find our host in the known hosts file */ | ||
while(!libssh2_knownhost_get(sshc->kh, &store, store)) { | ||
/* TODO: what about specific ports, mentioned in the name? */ | ||
if(strcmp(store->name, conn->host.name) == 0) { | ||
infof(data, "Found host (%s) in %s\n", | ||
conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]); | ||
found = true; | ||
break; | ||
} | ||
} | ||
|
||
if(found) { | ||
/* TODO: do we need to check the host and key format here as well?*/ | ||
switch(store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) { | ||
#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519 | ||
case LIBSSH2_KNOWNHOST_KEY_ED25519: | ||
hostkey_method = hostkey_method_ssh_ed25519; | ||
break; | ||
#endif | ||
#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521 | ||
case LIBSSH2_KNOWNHOST_KEY_ECDSA_521: | ||
hostkey_method = hostkey_method_ssh_ecdsa_521; | ||
break; | ||
#endif | ||
#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384 | ||
case LIBSSH2_KNOWNHOST_KEY_ECDSA_384: | ||
hostkey_method = hostkey_method_ssh_ecdsa_384; | ||
break; | ||
#endif | ||
#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256 | ||
case LIBSSH2_KNOWNHOST_KEY_ECDSA_256: | ||
hostkey_method = hostkey_method_ssh_ecdsa_256; | ||
break; | ||
#endif | ||
case LIBSSH2_KNOWNHOST_KEY_SSHRSA: | ||
hostkey_method = hostkey_method_ssh_rsa; | ||
break; | ||
case LIBSSH2_KNOWNHOST_KEY_SSHDSS: | ||
hostkey_method = hostkey_method_ssh_dss; | ||
break; | ||
case LIBSSH2_KNOWNHOST_KEY_RSA1: | ||
failf(data, | ||
"Found host key type RSA1 - no clue what to do with it...\n"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lose the "no clue". For example "Key type RSA1 is unsupported" or something like that. |
||
return CURLE_SSH; | ||
default: | ||
failf(data, "Unknown host key type: %i\n", | ||
(store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK)); | ||
return CURLE_SSH; | ||
} | ||
|
||
infof(data, "Set \"%s\" as SSH hostkey type\n", hostkey_method); | ||
/*TODO: should we add LIBSSH2_ERROR_METHOD_NOT_SUPPORTED | ||
* and LIBSSH2_ERROR_INVAL to libssh2_session_error_to_CURLE() | ||
* and is this even the right function at all? */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, if you can think of a better There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I could think of CURLE_BAD_FUNCTION_ARGUMENT, but I do not know if it would be useful at all. As you mentioned, the error string will help the most. |
||
result = libssh2_session_error_to_CURLE( | ||
libssh2_session_method_pref( | ||
sshc->ssh_session, LIBSSH2_METHOD_HOSTKEY, hostkey_method)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Break this up usually it's like Which reminds me should you set actualcode here? /cc @bagder |
||
} | ||
} | ||
return result; | ||
} | ||
|
||
/* | ||
* ssh_statemach_act() runs the SSH state machine as far as it can without | ||
* blocking and without reaching the end. The data the pointer 'block' points | ||
|
@@ -680,6 +780,12 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) | |
non-blocking */ | ||
libssh2_session_set_blocking(sshc->ssh_session, 0); | ||
|
||
result = ssh_force_knownhost_key_type(conn); | ||
if(result) { | ||
state(conn, SSH_SESSION_FREE); | ||
break; | ||
} | ||
|
||
state(conn, SSH_S_STARTUP); | ||
/* FALLTHROUGH */ | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo