summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authormichael musset <mickamusset@gmail.com>2021-11-04 15:55:47 +0100
committerDaniel Stenberg <daniel@haxx.se>2022-06-02 08:34:31 +0200
commit1544513958d9691f6ed576dfb3ff9f8c2a0de372 (patch)
tree2c8d0566bb542fab564d4ce8eb9f74595508c387 /lib
parent267d560b5ae4e73ea99f8211a2e8d0fa1305d9ea (diff)
downloadcurl-1544513958d9691f6ed576dfb3ff9f8c2a0de372.tar.gz
libssh2: add CURLOPT_SSH_HOSTKEYFUNCTION
The callback set by CURLOPT_SSH_HOSTKEYFUNCTION is called to check wether or not the connection should continue. The host key is passed in argument with a custom handle for the application. It overrides CURLOPT_SSH_KNOWNHOSTS Closes #7959
Diffstat (limited to 'lib')
-rw-r--r--lib/easyoptions.c6
-rw-r--r--lib/setopt.c12
-rw-r--r--lib/urldata.h5
-rw-r--r--lib/vssh/libssh2.c85
4 files changed, 91 insertions, 17 deletions
diff --git a/lib/easyoptions.c b/lib/easyoptions.c
index 04871ad1e..9ae6c6845 100644
--- a/lib/easyoptions.c
+++ b/lib/easyoptions.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* ___|___/|_| ______|
*
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -279,6 +279,8 @@ struct curl_easyoption Curl_easyopts[] = {
CURLOT_STRING, 0},
{"SSH_KEYDATA", CURLOPT_SSH_KEYDATA, CURLOT_CBPTR, 0},
{"SSH_KEYFUNCTION", CURLOPT_SSH_KEYFUNCTION, CURLOT_FUNCTION, 0},
+ {"SSH_HOSTKEYDATA", CURLOPT_SSH_HOSTKEYDATA, CURLOT_CBPTR, 0},
+ {"SSH_HOSTKEYFUNCTION", CURLOPT_SSH_HOSTKEYFUNCTION, CURLOT_FUNCTION, 0},
{"SSH_KNOWNHOSTS", CURLOPT_SSH_KNOWNHOSTS, CURLOT_STRING, 0},
{"SSH_PRIVATE_KEYFILE", CURLOPT_SSH_PRIVATE_KEYFILE, CURLOT_STRING, 0},
{"SSH_PUBLIC_KEYFILE", CURLOPT_SSH_PUBLIC_KEYFILE, CURLOT_STRING, 0},
@@ -360,6 +362,6 @@ struct curl_easyoption Curl_easyopts[] = {
*/
int Curl_easyopts_check(void)
{
- return ((CURLOPT_LASTENTRY%10000) != (315 + 1));
+ return ((CURLOPT_LASTENTRY%10000) != (317 + 1));
}
#endif
diff --git a/lib/setopt.c b/lib/setopt.c
index 19201393f..7dd9b5756 100644
--- a/lib/setopt.c
+++ b/lib/setopt.c
@@ -2466,7 +2466,19 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
result = Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS],
va_arg(param, char *));
break;
+#ifdef USE_LIBSSH2
+ case CURLOPT_SSH_HOSTKEYFUNCTION:
+ /* the callback to check the hostkey without the knownhost file */
+ data->set.ssh_hostkeyfunc = va_arg(param, curl_sshhostkeycallback);
+ break;
+ case CURLOPT_SSH_HOSTKEYDATA:
+ /*
+ * Custom client data to pass to the SSH keyfunc callback
+ */
+ data->set.ssh_hostkeyfunc_userp = va_arg(param, void *);
+ break;
+#endif
case CURLOPT_SSH_KEYFUNCTION:
/* setting to NULL is fine since the ssh.c functions themselves will
then revert to use the internal default */
diff --git a/lib/urldata.h b/lib/urldata.h
index a3cf56169..4e0d52266 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1758,6 +1758,11 @@ struct UserDefined {
int ftp_create_missing_dirs; /* 1 - create directories that don't exist
2 - the same but also allow MKD to fail once
*/
+#ifdef USE_LIBSSH2
+ curl_sshhostkeycallback ssh_hostkeyfunc; /* hostkey check callback */
+ void *ssh_hostkeyfunc_userp; /* custom pointer to callback */
+#endif
+
curl_sshkeycallback ssh_keyfunc; /* key matching callback */
void *ssh_keyfunc_userp; /* custom pointer to callback */
#ifndef CURL_DISABLE_NETRC
diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c
index d26926386..803cd5542 100644
--- a/lib/vssh/libssh2.c
+++ b/lib/vssh/libssh2.c
@@ -437,9 +437,45 @@ static int sshkeycallback(struct Curl_easy *easy,
#else
#define session_startup(x,y) libssh2_session_startup(x, (int)y)
#endif
+static int convert_ssh2_keytype(int sshkeytype)
+{
+ int keytype = CURLKHTYPE_UNKNOWN;
+ switch(sshkeytype) {
+ case LIBSSH2_HOSTKEY_TYPE_RSA:
+ keytype = CURLKHTYPE_RSA;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_DSS:
+ keytype = CURLKHTYPE_DSS;
+ break;
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
+ keytype = CURLKHTYPE_ECDSA;
+ break;
+#endif
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
+ keytype = CURLKHTYPE_ECDSA;
+ break;
+#endif
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_521
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_521:
+ keytype = CURLKHTYPE_ECDSA;
+ break;
+#endif
+#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
+ case LIBSSH2_HOSTKEY_TYPE_ED25519:
+ keytype = CURLKHTYPE_ED25519;
+ break;
+#endif
+ }
+ return keytype;
+}
static CURLcode ssh_knownhost(struct Curl_easy *data)
{
+ int sshkeytype = 0;
+ size_t keylen = 0;
+ int rc = 0;
CURLcode result = CURLE_OK;
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
@@ -448,11 +484,8 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
struct connectdata *conn = data->conn;
struct ssh_conn *sshc = &conn->proto.sshc;
struct libssh2_knownhost *host = NULL;
- int rc;
- int keytype;
- size_t keylen;
const char *remotekey = libssh2_session_hostkey(sshc->ssh_session,
- &keylen, &keytype);
+ &keylen, &sshkeytype);
int keycheck = LIBSSH2_KNOWNHOST_CHECK_FAILURE;
int keybit = 0;
@@ -464,12 +497,12 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
*/
enum curl_khmatch keymatch;
curl_sshkeycallback func =
- data->set.ssh_keyfunc?data->set.ssh_keyfunc:sshkeycallback;
+ data->set.ssh_keyfunc ? data->set.ssh_keyfunc : sshkeycallback;
struct curl_khkey knownkey;
struct curl_khkey *knownkeyp = NULL;
struct curl_khkey foundkey;
- switch(keytype) {
+ switch(sshkeytype) {
case LIBSSH2_HOSTKEY_TYPE_RSA:
keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA;
break;
@@ -533,16 +566,14 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) {
knownkey.key = host->key;
knownkey.len = 0;
- knownkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
- CURLKHTYPE_RSA : CURLKHTYPE_DSS;
+ knownkey.keytype = convert_ssh2_keytype(sshkeytype);
knownkeyp = &knownkey;
}
/* setup 'foundkey' */
foundkey.key = remotekey;
foundkey.len = keylen;
- foundkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
- CURLKHTYPE_RSA : CURLKHTYPE_DSS;
+ foundkey.keytype = convert_ssh2_keytype(sshkeytype);
/*
* if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the
@@ -639,7 +670,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
/* The fingerprint points to static storage (!), don't free() it. */
fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
- LIBSSH2_HOSTKEY_HASH_SHA256);
+ LIBSSH2_HOSTKEY_HASH_SHA256);
#else
const char *hostkey;
size_t len = 0;
@@ -654,8 +685,8 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
if(!fingerprint) {
failf(data,
- "Denied establishing ssh session: sha256 fingerprint "
- "not available");
+ "Denied establishing ssh session: sha256 fingerprint "
+ "not available");
state(data, SSH_SESSION_FREE);
sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
return sshc->actualcode;
@@ -715,7 +746,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
const char *fingerprint = NULL;
fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
- LIBSSH2_HOSTKEY_HASH_MD5);
+ LIBSSH2_HOSTKEY_HASH_MD5);
if(fingerprint) {
/* The fingerprint points to static storage (!), don't free() it. */
@@ -748,7 +779,31 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
}
if(!pubkey_md5 && !pubkey_sha256) {
- return ssh_knownhost(data);
+ if(data->set.ssh_hostkeyfunc) {
+ size_t keylen = 0;
+ int sshkeytype = 0;
+ int rc = 0;
+ /* we handle the process to the callback*/
+ const char *remotekey = libssh2_session_hostkey(sshc->ssh_session,
+ &keylen, &sshkeytype);
+ if(remotekey) {
+ int keytype = convert_ssh2_keytype(sshkeytype);
+ Curl_set_in_callback(data, true);
+ rc = data->set.ssh_hostkeyfunc(data->set.ssh_hostkeyfunc_userp,
+ keytype, remotekey, keylen);
+ Curl_set_in_callback(data, false);
+ if(rc!= CURLKHMATCH_OK) {
+ state(data, SSH_SESSION_FREE);
+ }
+ }
+ else {
+ state(data, SSH_SESSION_FREE);
+ }
+ return CURLE_OK;
+ }
+ else {
+ return ssh_knownhost(data);
+ }
}
else {
/* as we already matched, we skip the check for known hosts */