Callback for known hosts handling. See \fICURLOPT_SSH_KEYFUNCTION(3)\fP
.IP CURLOPT_SSH_KEYDATA
Custom pointer to pass to ssh key callback. See \fICURLOPT_SSH_KEYDATA(3)\fP
+.IP CURLOPT_SSH_HOSTKEYFUNCTION
+Callback for checking host key handling. See \fICURLOPT_SSH_HOSTKEYFUNCTION(3)\fP
+.IP CURLOPT_SSH_HOSTKEYDATA
+Custom pointer to pass to ssh host key callback. See \fICURLOPT_SSH_HOSTKEYDATA(3)\fP
.SH OTHER OPTIONS
.IP CURLOPT_PRIVATE
Private pointer to store. See \fICURLOPT_PRIVATE(3)\fP
--- /dev/null
+.\" **************************************************************************
+.\" * _ _ ____ _
+.\" * Project ___| | | | _ \| |
+.\" * / __| | | | |_) | |
+.\" * | (__| |_| | _ <| |___
+.\" * \___|\___/|_| \_\_____|
+.\" *
+.\" * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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
+.\" * are also available at https://curl.se/docs/copyright.html.
+.\" *
+.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+.\" * copies of the Software, and permit persons to whom the Software is
+.\" * furnished to do so, under the terms of the COPYING file.
+.\" *
+.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+.\" * KIND, either express or implied.
+.\" *
+.\" **************************************************************************
+.\"
+.TH CURLOPT_SSH_KEYDATA 3 "4 Nov 2021" "libcurl 7.84.0" "curl_easy_setopt options"
+.SH NAME
+CURLOPT_SSH_HOSTKEYDATA \- pointer to pass to the SSH hostkey callback
+.SH SYNOPSIS
+.nf
+#include <curl/curl.h>
+
+CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SSH_HOSTKEYDATA, void *pointer);
+.fi
+.SH DESCRIPTION
+Pass a void * as parameter. This \fIpointer\fP will be passed along verbatim
+to the callback set with \fICURLOPT_SSH_HOSTKEYFUNCTION(3)\fP.
+.SH DEFAULT
+NULL
+.SH PROTOCOLS
+SCP and SFTP
+.SH EXAMPLE
+.nf
+int sshhostkeycallback(void *clientp,/* custom pointer passed with CURLOPT_SSH_HOSTKEYDATA */
+ int keytype, /* CURLKHTYPE */
+ const char * key, /*hostkey to check*/
+ size_t keylen); /*length of the key*/
+{
+ /* 'clientp' points to the callback_data struct */
+ /* investigate the situation and return the correct value */
+ return CURLKHMATCH_OK;
+}
+{
+ curl_easy_setopt(curl, CURLOPT_URL, "sftp://example.com/thisfile.txt");
+ curl_easy_setopt(curl, CURLOPT_SSH_HOSTKEYFUNCTION, sshhostkeycallback);
+ curl_easy_setopt(curl, CURLOPT_SSH_HOSTKEYDATA, &callback_data);
+
+ curl_easy_perform(curl);
+}
+.fi
+.SH AVAILABILITY
+Added in 7.84.0 , work only with libssh2 backend.
+.SH RETURN VALUE
+Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
+.SH "SEE ALSO"
+.BR CURLOPT_SSH_HOSTKEYFUNCTION "(3), "
--- /dev/null
+.\" **************************************************************************
+.\" * _ _ ____ _
+.\" * Project ___| | | | _ \| |
+.\" * / __| | | | |_) | |
+.\" * | (__| |_| | _ <| |___
+.\" * \___|\___/|_| \_\_____|
+.\" *
+.\" * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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
+.\" * are also available at https://curl.se/docs/copyright.html.
+.\" *
+.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+.\" * copies of the Software, and permit persons to whom the Software is
+.\" * furnished to do so, under the terms of the COPYING file.
+.\" *
+.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+.\" * KIND, either express or implied.
+.\" *
+.\" **************************************************************************
+.\"
+.TH CURLOPT_SSH_HOSTKEYFUNCTION 3 "4 Nov 2021" "libcurl 7.84.0" "curl_easy_setopt options"
+.SH NAME
+CURLOPT_SSH_HOSTKEYFUNCTION \- callback to check hostkey (to overwrite the functionality known_host file)
+.SH SYNOPSIS
+.nf
+#include <curl/curl.h>
+
+int sshhostkeycallback(void *clientp,/* custom pointer passed with CURLOPT_SSH_HOSTKEYDATA */
+ int keytype, /* CURLKHTYPE */
+ const char * key, /*hostkey to check*/
+ size_t keylen); /*length of the key*/
+
+CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SSH_HOSTKEYFUNCTION,
+ sshhostkeycallback);
+.fi
+.SH DESCRIPTION
+Pass a pointer to your callback function, which should match the prototype
+shown above. It overrides CURLOPT_SSH_KNOWNHOSTS.
+
+It gets called when the verification of the hostkey is needed.
+
+This callback function gets passed a custom pointer (set with
+\fICURLOPT_SSH_HOSTKEYDATA(3)\fP, the libcurl keytype, the hostkey,
+the length of the key.
+It MUST return one of the following return codes to tell libcurl how to act:
+.IP CURLKHMATCH_OK
+The hostkey is accepted, the connexion should continue.
+.IP CURLKHMATCH_MISMATCH
+the hostkey is rejected, the connexion is canceled.
+.SH DEFAULT
+NULL
+.SH PROTOCOLS
+SCP and SFTP
+.SH EXAMPLE
+.nf
+int sshhostkeycallback(void *clientp,/* custom pointer passed with CURLOPT_SSH_HOSTKEYDATA */
+ int keytype, /* CURLKHTYPE */
+ const char * key, /*hostkey to check*/
+ size_t keylen); /*length of the key*/
+{
+ /* 'clientp' points to the callback_data struct */
+ /* investigate the situation and return the correct value */
+ return CURLKHMATCH_OK;
+}
+{
+ curl_easy_setopt(curl, CURLOPT_URL, "sftp://example.com/thisfile.txt");
+ curl_easy_setopt(curl, CURLOPT_SSH_HOSTKEYFUNCTION, sshhostkeycallback);
+ curl_easy_setopt(curl, CURLOPT_SSH_HOSTKEYDATA, &callback_data);
+
+ curl_easy_perform(curl);
+}
+.fi
+.SH AVAILABILITY
+Added in 7.84.0 , work only with libssh2 backend.
+.SH RETURN VALUE
+Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
+.SH "SEE ALSO"
+.BR CURLOPT_SSH_HOSTKEYDATA "(3), "
CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256 7.80.0
CURLOPT_SSH_KEYDATA 7.19.6
CURLOPT_SSH_KEYFUNCTION 7.19.6
+CURLOPT_SSH_HOSTKEYFUNCTION 7.84.0
+CURLOPT_SSH_HOSTKEYDATA 7.84.0
CURLOPT_SSH_KNOWNHOSTS 7.19.6
CURLOPT_SSH_PRIVATE_KEYFILE 7.16.1
CURLOPT_SSH_PUBLIC_KEYFILE 7.16.1
const struct curl_khkey *knownkey, /* known */
const struct curl_khkey *foundkey, /* found */
enum curl_khmatch, /* libcurl's view on the keys */
- void *clientp); /* custom pointer passed from app */
+ void *clientp); /* custom pointer passed with */
+ /* CURLOPT_SSH_KEYDATA */
+
+typedef int
+ (*curl_sshhostkeycallback) (void *clientp,/* custom pointer passed*/
+ /* with CURLOPT_SSH_HOSTKEYDATA */
+ int keytype, /* CURLKHTYPE */
+ const char *key, /*hostkey to check*/
+ size_t keylen); /*length of the key*/
+ /*return CURLE_OK to accept*/
+ /*or something else to refuse*/
+
/* parameter for the CURLOPT_USE_SSL option */
typedef enum {
/* Set MIME option flags. */
CURLOPT(CURLOPT_MIME_OPTIONS, CURLOPTTYPE_LONG, 315),
+ /* set the SSH host key callback, must point to a curl_sshkeycallback
+ function */
+ CURLOPT(CURLOPT_SSH_HOSTKEYFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 316),
+
+ /* set the SSH host key callback custom pointer */
+ CURLOPT(CURLOPT_SSH_HOSTKEYDATA, CURLOPTTYPE_CBPOINT, 317),
+
CURLOPT_LASTENTRY /* the last unused */
} CURLoption;
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.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
(option) == CURLOPT_WRITEDATA || \
(option) == CURLOPT_RESOLVER_START_DATA || \
(option) == CURLOPT_TRAILERDATA || \
+ (option) == CURLOPT_SSH_HOSTKEYDATA || \
0)
/* evaluates to true if option takes a POST data argument (void* or char*) */
* | (__| |_| | _ <| |___
* ___|___/|_| ______|
*
- * 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
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},
*/
int Curl_easyopts_check(void)
{
- return ((CURLOPT_LASTENTRY%10000) != (315 + 1));
+ return ((CURLOPT_LASTENTRY%10000) != (317 + 1));
}
#endif
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 */
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
#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
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;
*/
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;
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
#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;
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;
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. */
}
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 */
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 2017 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) 2017 - 2022, Daniel Stenberg, <daniel@haxx.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
static curl_opensocket_callback opensocketcb;
static curl_seek_callback seekcb;
static curl_sshkeycallback ssh_keycb;
+static curl_sshhostkeycallback ssh_hostkeycb;
static curl_chunk_bgn_callback chunk_bgn_cb;
static curl_chunk_end_callback chunk_end_cb;
static curl_fnmatch_callback fnmatch_cb;