mirror of
https://github.com/curl/curl.git
synced 2026-08-25 23:23:31 +03:00
- Added CURLOPT_SSH_KNOWNHOSTS, CURLOPT_SSH_KEYFUNCTION, CURLOPT_SSH_KEYDATA.
They introduce known_host support for SSH keys to libcurl. See docs for details.
This commit is contained in:
parent
9cff716925
commit
47c392e135
9 changed files with 311 additions and 14 deletions
157
lib/ssh.c
157
lib/ssh.c
|
|
@ -306,6 +306,7 @@ static void state(struct connectdata *conn, sshstate nowstate)
|
|||
static const char * const names[] = {
|
||||
"SSH_STOP",
|
||||
"SSH_S_STARTUP",
|
||||
"SSH_HOSTKEY",
|
||||
"SSH_AUTHLIST",
|
||||
"SSH_AUTH_PKEY_INIT",
|
||||
"SSH_AUTH_PKEY",
|
||||
|
|
@ -433,6 +434,21 @@ static CURLcode ssh_getworkingpath(struct connectdata *conn,
|
|||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static int sshkeycallback(CURL *easy,
|
||||
const struct curl_khkey *knownkey, /* known */
|
||||
const struct curl_khkey *foundkey, /* found */
|
||||
enum curl_khmatch match,
|
||||
void *clientp)
|
||||
{
|
||||
(void)easy;
|
||||
(void)knownkey;
|
||||
(void)foundkey;
|
||||
(void)clientp;
|
||||
|
||||
/* we only allow perfect matches, and we reject everything else */
|
||||
return (match != CURLKHMATCH_OK)?CURLKHSTAT_REJECT:CURLKHSTAT_FINE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Earlier libssh2 versions didn't have the ability to seek to 64bit positions
|
||||
* with 32bit size_t.
|
||||
|
|
@ -483,9 +499,15 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
|
|||
break;
|
||||
}
|
||||
|
||||
/* Set libssh2 to non-blocking, since cURL is all non-blocking */
|
||||
/* Set libssh2 to non-blocking, since everything internally is
|
||||
non-blocking */
|
||||
libssh2_session_set_blocking(sshc->ssh_session, 0);
|
||||
|
||||
state(conn, SSH_HOSTKEY);
|
||||
|
||||
/* fall-through */
|
||||
case SSH_HOSTKEY:
|
||||
|
||||
#ifdef CURL_LIBSSH2_DEBUG
|
||||
/*
|
||||
* Before we authenticate we should check the hostkey's fingerprint
|
||||
|
|
@ -527,12 +549,121 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
|
||||
if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
|
||||
/* we're asked to verify the host against a file */
|
||||
int keytype;
|
||||
size_t keylen;
|
||||
const char *remotekey = libssh2_session_hostkey(sshc->ssh_session,
|
||||
&keylen, &keytype);
|
||||
int keycheck;
|
||||
int keybit;
|
||||
|
||||
if(remotekey) {
|
||||
/*
|
||||
* A subject to figure out is what host name we need to pass in here.
|
||||
* What host name does OpenSSH store in its file if an IDN name is
|
||||
* used?
|
||||
*/
|
||||
struct libssh2_knownhost *host;
|
||||
enum curl_khmatch keymatch;
|
||||
curl_sshkeycallback func =
|
||||
data->set.ssh_keyfunc?data->set.ssh_keyfunc:sshkeycallback;
|
||||
struct curl_khkey knownkey;
|
||||
struct curl_khkey *knownkeyp = NULL;
|
||||
struct curl_khkey foundkey;
|
||||
|
||||
keybit = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
|
||||
LIBSSH2_KNOWNHOST_KEY_SSHRSA:LIBSSH2_KNOWNHOST_KEY_SSHDSS;
|
||||
|
||||
keycheck = libssh2_knownhost_check(sshc->kh,
|
||||
conn->host.name,
|
||||
remotekey, keylen,
|
||||
LIBSSH2_KNOWNHOST_TYPE_PLAIN|
|
||||
LIBSSH2_KNOWNHOST_KEYENC_RAW|
|
||||
keybit,
|
||||
&host);
|
||||
|
||||
infof(data, "SSH host check: %d, key: %s\n", keycheck,
|
||||
(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)?
|
||||
host->key:"<none>");
|
||||
|
||||
/* setup 'knownkey' */
|
||||
if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) {
|
||||
knownkey.key = host->key;
|
||||
knownkey.len = 0;
|
||||
knownkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
|
||||
CURLKHTYPE_RSA : CURLKHTYPE_DSS;
|
||||
knownkeyp = &knownkey;
|
||||
}
|
||||
|
||||
/* setup 'foundkey' */
|
||||
foundkey.key = remotekey;
|
||||
foundkey.len = keylen;
|
||||
foundkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
|
||||
CURLKHTYPE_RSA : CURLKHTYPE_DSS;
|
||||
|
||||
/*
|
||||
* if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the
|
||||
* curl_khmatch enum are ever modified, we need to introduce a
|
||||
* translation table here!
|
||||
*/
|
||||
keymatch = (enum curl_khmatch)keycheck;
|
||||
|
||||
/* Ask the callback how to behave */
|
||||
rc = func(data, knownkeyp, /* from the knownhosts file */
|
||||
&foundkey, /* from the remote host */
|
||||
keymatch, data->set.ssh_keyfunc_userp);
|
||||
}
|
||||
else
|
||||
/* no remotekey means failure! */
|
||||
rc = CURLKHSTAT_REJECT;
|
||||
|
||||
switch(rc) {
|
||||
default: /* unknown return codes will equal reject */
|
||||
case CURLKHSTAT_REJECT:
|
||||
state(conn, SSH_SESSION_FREE);
|
||||
case CURLKHSTAT_DEFER:
|
||||
/* DEFER means bail out but keep the SSH_HOSTKEY state */
|
||||
result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
|
||||
break;
|
||||
case CURLKHSTAT_FINE:
|
||||
case CURLKHSTAT_FINE_ADD_TO_FILE:
|
||||
/* proceed */
|
||||
if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) {
|
||||
/* the found host+key didn't match but has been told to be fine
|
||||
anyway so we add it in memory */
|
||||
int addrc = libssh2_knownhost_add(sshc->kh,
|
||||
conn->host.name, NULL,
|
||||
remotekey, keylen,
|
||||
LIBSSH2_KNOWNHOST_TYPE_PLAIN|
|
||||
LIBSSH2_KNOWNHOST_KEYENC_RAW|
|
||||
keybit, NULL);
|
||||
if(addrc)
|
||||
infof(data, "Warning adding the known host %s failed!\n",
|
||||
conn->host.name);
|
||||
else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE) {
|
||||
/* now we write the entire in-memory list of known hosts to the
|
||||
known_hosts file */
|
||||
int wrc =
|
||||
libssh2_knownhost_writefile(sshc->kh,
|
||||
data->set.str[STRING_SSH_KNOWNHOSTS],
|
||||
LIBSSH2_KNOWNHOST_FILE_OPENSSH);
|
||||
if(wrc) {
|
||||
infof(data, "Warning, writing %s failed!\n",
|
||||
data->set.str[STRING_SSH_KNOWNHOSTS]);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
|
||||
|
||||
state(conn, SSH_AUTHLIST);
|
||||
break;
|
||||
|
||||
case SSH_AUTHLIST:
|
||||
/* TBD - methods to check the host keys need to be done */
|
||||
|
||||
/*
|
||||
* Figure out authentication methods
|
||||
* NB: As soon as we have provided a username to an openssh server we
|
||||
|
|
@ -2278,6 +2409,26 @@ static CURLcode ssh_connect(struct connectdata *conn, bool *done)
|
|||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
|
||||
if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
|
||||
int rc;
|
||||
ssh->kh = libssh2_knownhost_init(ssh->ssh_session);
|
||||
if(!ssh->kh) {
|
||||
/* eeek. TODO: free the ssh_session! */
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
|
||||
/* read all known hosts from there */
|
||||
rc = libssh2_knownhost_readfile(ssh->kh,
|
||||
data->set.str[STRING_SSH_KNOWNHOSTS],
|
||||
LIBSSH2_KNOWNHOST_FILE_OPENSSH);
|
||||
if(rc) {
|
||||
infof(data, "Failed to read known hosts from %s\n",
|
||||
data->set.str[STRING_SSH_KNOWNHOSTS]);
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
|
||||
|
||||
#ifdef CURL_LIBSSH2_DEBUG
|
||||
libssh2_trace(ssh->ssh_session, ~0);
|
||||
infof(data, "SSH socket: %d\n", sock);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue