GNURLOPT_SSH_KEYFUNCTION(3)

πŸ“¦ version 7.72.0, in nixos 26.05, nixpkgs unstable

πŸ’… GNURLOPT_SSH_KEYFUNCTION β€” bestie, this is your known-hosts vibe check πŸ¦‹

ok so real talk, this page is from the curl_easy_setopt options fam, and it's literally the CURLOPT_SSH_KEYFUNCTION era of your life. Let's get into it, no cap.


✨ NAME ✨

CURLOPT_SSH_KEYFUNCTION β€” the callback for known host matching logic. yeah. it's the "so what do we do about this host" hotline πŸ”₯


πŸŽ€ SYNOPSIS πŸŽ€

ok first of all, the include. it's giving gnurl. not curl. gnurl. iykyk πŸ’…

#include <gnurl/curl.h>

enum curl_khstat {
  CURLKHSTAT_FINE_ADD_TO_FILE,
  CURLKHSTAT_FINE,
  CURLKHSTAT_REJECT, /* reject the connection, return an error */
  CURLKHSTAT_DEFER,  /* do not accept it, but we can't answer right
                        now so this causes a CURLE_DEFER error but
                        otherwise the connection will be left intact
                        etc */
};

enum curl_khmatch {
  CURLKHMATCH_OK,       /* match */
  CURLKHMATCH_MISMATCH, /* host found, key mismatch! */
  CURLKHMATCH_MISSING,  /* no matching host/key found */
};

struct curl_khkey {
  const char *key; /* points to a null-terminated string encoded with
                      base64 if len is zero, otherwise to the "raw"
                      data */
  size_t len;
  enum curl_khtype keytype;
};

int ssh_keycallback(CURL *easy,
                    const struct curl_khkey *knownkey,
                    const struct curl_khkey *foundkey,
                    enum curl_khmatch,
                    void *clientp);

CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SSH_KEYFUNCTION,
                          ssh_keycallback);

like, look at that prototype. that's your callback's whole job description 🫢


🌸 DESCRIPTION 🌸

ok so you're gonna pass a pointer to your callback function, and it has to match the prototype up there. periodt.

it gets called when the known_host matching has been done β€” so basically once libcurl has done the vibe check, it hits you up to let you act on it and decide how libcurl should proceed. and important: the callback will only be called if CURLOPT_SSH_KNOWNHOSTS(3) is also set. so like, no known_hosts, no call. that's the rule πŸ’€

so what does your callback get handed? a whole little gift bag:

  • the CURL handle
  • knownkey β€” the key from the known_hosts file
  • foundkey β€” the key from the remote site
  • info from libcurl on the matching status
  • a custom pointer (the one you set with CURLOPT_SSH_KEYDATA(3))

and it MUST return one of the following return codes to tell libcurl how to act. must. like, capital M must. don't leave your girl hanging πŸ™ˆ

CURLKHSTAT_FINE_ADD_TO_FILE

the host+key is accepted, and libcurl will append it to the known_hosts file before continuing with the connection. it'll also add the host+key combo to the known_host pool kept in memory if it wasn't already there. πŸ’–

⚠️ ok heads up though: the adding of data to the file is done by completely replacing the file with a new copy, so the permissions of the file must allow this. if they don't, that's a you problem, bestie.

CURLKHSTAT_FINE

the host+key is accepted, libcurl will continue with the connection. it'll also add the host+key combo to the known_host pool kept in memory if it wasn't already in there. so like, FINE is FINE_ADD_TO_FILE's chill sibling who doesn't touch the actual file ✨

CURLKHSTAT_REJECT

the host+key is rejected. libcurl will deny the connection from continuing and it will be closed. bye bestie πŸ‘‹πŸ’€

CURLKHSTAT_DEFER

the host+key is rejected, but the SSH connection is asked to be kept alive. 🫢

this one's for when the app wants to somehow return back and act on the host+key situation, and then retry without needing the overhead of setting it all up from scratch again. iconic lazy-genius behavior honestly.


πŸ¦‹ DEFAULT πŸ¦‹

NULL

as in, nothing. nada. the void. πŸ•³οΈ


🧚 PROTOCOLS 🧚

SFTP and SCP β€” that's it, that's the list ✨


🌈 EXAMPLE 🌈

ok here's the callback, looking all responsible:

static int keycb(CURL *easy,
                 const struct curl_khkey *knownkey,
                 const struct curl_khkey *foundkey,
                 enum curl_khmatch,
                 void *clientp)
{
  /* 'clientp' points to the callback_data struct */
  /* investigate the situation and return the correct value */
  return CURLKHSTAT_FINE_ADD_TO_FILE;
}

and then the actual setup part, where you wire it all up:

{
  curl_easy_setopt(curl, CURLOPT_URL, "sftp://example.com/thisfile.txt");
  curl_easy_setopt(curl, CURLOPT_SSH_KEYFUNCTION, keycb);
  curl_easy_setopt(curl, CURLOPT_SSH_KEYDATA, &callback_data);
  curl_easy_setopt(curl, CURLOPT_SSH_KNOWNHOSTS, "/home/user/known_hosts");

  curl_easy_perform(curl);
}

see how CURLOPT_SSH_KNOWNHOSTS is right there? because remember, no known_hosts = no callback. main character energy requires the supporting cast 🎬


πŸ’œ AVAILABILITY πŸ’œ

added in 7.19.6. vintage but still slaying ⭐


πŸ‘‘ RETURN VALUE πŸ‘‘

returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not. simple as that, ngl.


πŸ“ SEE ALSO πŸ“

go read those too, they're literally the other half of this story 🫢


and that's the page!! πŸŽ€ this whole thing came to you courtesy of libcurl 7.37.0, dated 19 Jun 2014, living its best life under the GNURLOPT_SSH_KEYFUNCTION(3) name. mwah πŸ’‹ ✨

πŸ“¦ where this page comes from