]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
libssh2: add CURLOPT_SSH_HOSTKEYFUNCTION
authormichael musset <mickamusset@gmail.com>
Thu, 4 Nov 2021 14:55:47 +0000 (15:55 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 2 Jun 2022 06:34:31 +0000 (08:34 +0200)
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

docs/libcurl/curl_easy_setopt.3
docs/libcurl/opts/CURLOPT_SSH_HOSTKEYDATA.3 [new file with mode: 0644]
docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.3 [new file with mode: 0644]
docs/libcurl/symbols-in-versions
include/curl/curl.h
include/curl/typecheck-gcc.h
lib/easyoptions.c
lib/setopt.c
lib/urldata.h
lib/vssh/libssh2.c
tests/libtest/mk-lib1521.pl

index 66cb466c01e8a52de78cbf5eaff750a610490a05..2f118ca0dbd3761e2748744f7ca94dd1d1ac8476 100644 (file)
@@ -664,6 +664,10 @@ File name with known hosts. See \fICURLOPT_SSH_KNOWNHOSTS(3)\fP
 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
diff --git a/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYDATA.3 b/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYDATA.3
new file mode 100644 (file)
index 0000000..412cc36
--- /dev/null
@@ -0,0 +1,63 @@
+.\" **************************************************************************
+.\" *                                  _   _ ____  _
+.\" *  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), "
diff --git a/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.3 b/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.3
new file mode 100644 (file)
index 0000000..c27e2fd
--- /dev/null
@@ -0,0 +1,80 @@
+.\" **************************************************************************
+.\" *                                  _   _ ____  _
+.\" *  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), "
index c4d127f6f4fb866bfec87e19eb40d466795c42c2..d7c88ccde2d64b8966de765fb7ab9e6a8ebc9a08 100644 (file)
@@ -799,6 +799,8 @@ CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 7.17.1
 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
index fcd03f736280cc34bdfb95ee650390141ed3e593..cb26d24d509125536d00cecff27e9508a6d0dd9e 100644 (file)
@@ -855,7 +855,18 @@ typedef int
                           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 {
@@ -2122,6 +2133,13 @@ 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;
 
index 9e14d8a3726ce36760f7cecbf588f72575eecf43..43a920104da6ce2b2eb36aaaa1640675912b0b38 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * 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
@@ -374,6 +374,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
    (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*) */
index 04871ad1e3b7891ae70847102f046b46a64adefa..9ae6c68457c903e800660115cee998f056626568 100644 (file)
@@ -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
index 19201393f5c9dab0fa5058b074e1178d21a3fbdf..7dd9b57568e90083b24a25060e712527670b1b57 100644 (file)
@@ -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 */
index a3cf56169b8e4059a36a27b7cf01428e82636eef..4e0d522661179e18dd12738feca4657ef56d7688 100644 (file)
@@ -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
index d269263864716e41dd96c3f924d1d8f688259145..803cd5542910775fe1b581cedcb49fa4d110c89c 100644 (file)
@@ -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 */
index 0122fc3751333befeff62fc4d8ca0e97b9bb3789..4eab21806d5b9945addc5f6ae13c98b50e5ad3cb 100755 (executable)
@@ -6,7 +6,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# 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
@@ -128,6 +128,7 @@ static curl_sockopt_callback sockoptcb;
 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;