SSH: move knownhost logic to separate function
This commit is contained in:
parent
b903186fa0
commit
9869668884
238
lib/ssh.c
238
lib/ssh.c
@ -504,6 +504,129 @@ static int sshkeycallback(CURL *easy,
|
|||||||
#define libssh2_session_startup(x,y) libssh2_session_handshake(x,y)
|
#define libssh2_session_startup(x,y) libssh2_session_handshake(x,y)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static CURLcode ssh_knownhost(struct connectdata *conn)
|
||||||
|
{
|
||||||
|
struct SessionHandle *data = conn->data;
|
||||||
|
CURLcode result = CURLE_OK;
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
|
||||||
|
if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
|
||||||
|
/* we're asked to verify the host against a file */
|
||||||
|
struct ssh_conn *sshc = &conn->proto.sshc;
|
||||||
|
int rc;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else /* HAVE_LIBSSH2_KNOWNHOST_API */
|
||||||
|
(void)conn;
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ssh_statemach_act() runs the SSH state machine as far as it can without
|
* 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
|
* blocking and without reaching the end. The data the pointer 'block' points
|
||||||
@ -595,118 +718,9 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
|
result = ssh_knownhost(conn);
|
||||||
if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
|
if(!result)
|
||||||
/* we're asked to verify the host against a file */
|
state(conn, SSH_AUTHLIST);
|
||||||
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;
|
break;
|
||||||
|
|
||||||
case SSH_AUTHLIST:
|
case SSH_AUTHLIST:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user