diff options
author | Daniel Stenberg <daniel@haxx.se> | 2009-07-22 22:49:01 +0000 |
---|---|---|
committer | Daniel Stenberg <daniel@haxx.se> | 2009-07-22 22:49:01 +0000 |
commit | 47c392e135f17d400acc5d7e99b88d16578cb406 (patch) | |
tree | e4069b5c9fa7582f53ff88eb8c08f86d43f8ea55 /lib/ssh.c | |
parent | 9cff716925d8501992a10f09a53d4ad3ed3d919f (diff) |
- Added CURLOPT_SSH_KNOWNHOSTS, CURLOPT_SSH_KEYFUNCTION, CURLOPT_SSH_KEYDATA.
They introduce known_host support for SSH keys to libcurl. See docs for
details.
Diffstat (limited to 'lib/ssh.c')
-rw-r--r-- | lib/ssh.c | 157 |
1 files changed, 154 insertions, 3 deletions
@@ -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); |