← Commit list

eb9b253d66

Viktor Szakats
Viktor Szakats
2026-05-15
SUCCESS Duration: 213s

Subject

libssh: add support for SHA256 host public keys

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

.github/workflows/windows.yml+1 -3
@@ -522,11 +522,9 @@ jobs:
fi
fi
if [ -n "${MATRIX_OPENSSH}" ]; then # OpenSSH-Windows
- TFLAGS+=' ~601 ~603 ~617 ~619 ~621 ~641 ~665 ~2004' # SCP
+ TFLAGS+=' ~601 ~603 ~617 ~619 ~621 ~641 ~665 ~2004 ~3022' # SCP
if [[ "${MATRIX_INSTALL} " = *'libssh '* ]]; then
TFLAGS+=' ~614' # 'SFTP pre-quote chmod' SFTP, pre-quote, directory
- else
- TFLAGS+=' ~3022' # 'SCP correct sha256 host key' SCP, server sha256 key check
fi
fi
if [ "${MATRIX_OPENSSH}" = 'OpenSSH-Windows' ]; then
docs/cmdline-opts/hostpubsha256.md+0 -3
@@ -18,6 +18,3 @@ Example:
Pass a string containing a Base64-encoded SHA256 hash of the remote host's
public key. curl refuses the connection with the host unless the hashes match.
-
-This feature requires libcurl to be built with libssh2 and does not work with
-other SSH backends.
docs/libcurl/opts/CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256.md+0 -4
@@ -73,10 +73,6 @@ int main(void)
}
~~~
-# NOTES
-
-Requires the libssh2 backend.
-
# %AVAILABILITY%
# RETURN VALUE
lib/setopt.c+6 -6
@@ -2342,6 +2342,12 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
* for validation purposes.
*/
return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_MD5], ptr);
+ case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256:
+ /*
+ * Option to allow for the SHA256 of the host public key to be checked
+ * for validation purposes.
+ */
+ return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], ptr);
case CURLOPT_SSH_KNOWNHOSTS:
/*
* Store the filename to read known hosts from.
@@ -2349,12 +2355,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
return Curl_setstropt(&s->str[STRING_SSH_KNOWNHOSTS], ptr);
#endif
#ifdef USE_LIBSSH2
- case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256:
- /*
- * Option to allow for the SHA256 of the host public key to be checked
- * for validation purposes.
- */
- return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], ptr);
case CURLOPT_SSH_HOSTKEYDATA:
/*
* Custom client data to pass to the SSH keyfunc callback
lib/vssh/libssh.c+74 -12
@@ -57,6 +57,7 @@
#include "multiif.h"
#include "select.h"
#include "vssh/vssh.h"
+#include "curlx/base64.h" /* for curlx_base64_encode() */
#ifdef HAVE_UNISTD_H
#include <unistd.h>
@@ -109,12 +110,14 @@ static CURLcode sftp_error_to_CURLE(int err)
}
/* Multiple options:
- * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5
+ * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256] is set with a SHA256
+ * hash.
+ * 2. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5
* hash (90s style auth, not sure we should have it here)
- * 2. data->set.ssh_keyfunc callback is set. Then we do trust on first
+ * 3. data->set.ssh_keyfunc callback is set. Then we do trust on first
* use. We even save on knownhosts if CURLKHSTAT_FINE_ADD_TO_FILE
* is returned by it.
- * 3. none of the above. We only accept if it is present on known hosts.
+ * 4. none of the above. We only accept if it is present on known hosts.
*
* Returns SSH_OK or SSH_ERROR.
*/
@@ -122,8 +125,10 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc)
{
int rc;
ssh_key pubkey;
- size_t hlen;
- unsigned char *hash = NULL;
+ unsigned char *hash_sha256 = NULL;
+ size_t hlen_sha256;
+ unsigned char *hash_md5 = NULL;
+ size_t hlen_md5;
char *found_base64 = NULL;
char *known_base64 = NULL;
int vstate;
@@ -139,20 +144,75 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc)
if(rc != SSH_OK)
return rc;
+ if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]) {
+ const char *pubkey_sha256 =
+ data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256];
+ char *fingerprint_b64 = NULL;
+ size_t fingerprint_b64_len;
+ size_t pub_pos = 0;
+ size_t b64_pos = 0;
+
+ rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_SHA256,
+ &hash_sha256, &hlen_sha256);
+ if(rc != SSH_OK || hlen_sha256 != 32) {
+ failf(data, "Denied establishing ssh session: "
+ "SHA256 fingerprint not available");
+ goto cleanup;
+ }
+
+ if(curlx_base64_encode((const uint8_t *)hash_sha256, 32, &fingerprint_b64,
+ &fingerprint_b64_len) != CURLE_OK) {
+ rc = SSH_ERROR;
+ goto cleanup;
+ }
+
+ infof(data, "SSH SHA256 fingerprint: %s", fingerprint_b64);
+
+ /* Find the position of any = padding characters in the public key */
+ while((pubkey_sha256[pub_pos] != '=') && pubkey_sha256[pub_pos]) {
+ pub_pos++;
+ }
+
+ /* Find the position of any = padding characters in the base64 coded
+ * hostkey fingerprint */
+ while((fingerprint_b64[b64_pos] != '=') && fingerprint_b64[b64_pos]) {
+ b64_pos++;
+ }
+
+ /* Before we authenticate we check the hostkey's SHA256 fingerprint
+ * against a known fingerprint, if available.
+ */
+ if((pub_pos != b64_pos) ||
+ strncmp(fingerprint_b64, pubkey_sha256, pub_pos)) {
+ failf(data,
+ "Denied establishing ssh session: mismatch SHA256 fingerprint. "
+ "Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256);
+ curlx_free(fingerprint_b64);
+ rc = SSH_ERROR;
+ goto cleanup;
+ }
+
+ curlx_free(fingerprint_b64);
+
+ rc = SSH_OK;
+ goto cleanup;
+ }
+
if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) {
- int i;
- char md5buffer[33];
const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
+ char md5buffer[33];
+ int i;
- rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, &hash, &hlen);
- if(rc != SSH_OK || hlen != 16) {
+ rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5,
+ &hash_md5, &hlen_md5);
+ if(rc != SSH_OK || hlen_md5 != 16) {
failf(data,
"Denied establishing ssh session: MD5 fingerprint not available");
goto cleanup;
}
for(i = 0; i < 16; i++)
- curl_msnprintf(&md5buffer[i * 2], 3, "%02x", hash[i]);
+ curl_msnprintf(&md5buffer[i * 2], 3, "%02x", hash_md5[i]);
infof(data, "SSH MD5 fingerprint: %s", md5buffer);
@@ -297,8 +357,10 @@ cleanup:
/* !checksrc! disable BANNEDFUNC 1 */
free(known_base64); /* allocated by libssh, deallocate with system free */
}
- if(hash)
- ssh_clean_pubkey_hash(&hash);
+ if(hash_sha256)
+ ssh_clean_pubkey_hash(&hash_sha256);
+ if(hash_md5)
+ ssh_clean_pubkey_hash(&hash_md5);
ssh_key_free(pubkey);
if(knownhostsentry) {
ssh_knownhosts_entry_free(knownhostsentry);
lib/vssh/libssh2.c+1 -1
@@ -57,7 +57,7 @@
#include "curlx/fopen.h"
#include "vssh/vssh.h"
#include "curlx/strparse.h"
-#include "curlx/base64.h" /* for base64 encoding/decoding */
+#include "curlx/base64.h" /* for curlx_base64_encode() */
static const char *sftp_libssh2_strerror(unsigned long err)
{
src/tool_getparam.c+1 -4
@@ -2768,10 +2768,7 @@ static ParameterError opt_string(struct OperationConfig *config,
}
break;
case C_HOSTPUBSHA256: /* --hostpubsha256 */
- if(!feature_libssh2)
- err = PARAM_LIBCURL_DOESNT_SUPPORT;
- else
- err = getstr(&config->hostpubsha256, nextarg, DENY_BLANK);
+ err = getstr(&config->hostpubsha256, nextarg, DENY_BLANK);
break;
case C_TLSUSER: /* --tlsuser */
if(!feature_tls_srp)
src/tool_libinfo.c+0 -4
@@ -71,7 +71,6 @@ bool feature_http2 = FALSE;
bool feature_http3 = FALSE;
bool feature_httpsproxy = FALSE;
bool feature_libz = FALSE;
-bool feature_libssh2 = FALSE;
bool feature_ntlm = FALSE;
bool feature_ntlm_wb = FALSE;
bool feature_spnego = FALSE;
@@ -183,9 +182,6 @@ CURLcode get_libcurl_info(void)
++feature_count;
}
- feature_libssh2 = curlinfo->age >= CURLVERSION_FOURTH &&
- curlinfo->libssh_version &&
- !strncmp("libssh2", curlinfo->libssh_version, 7);
return CURLE_OK;
}
src/tool_libinfo.h+0 -1
@@ -54,7 +54,6 @@ extern bool feature_http2;
extern bool feature_http3;
extern bool feature_httpsproxy;
extern bool feature_libz;
-extern bool feature_libssh2;
extern bool feature_ntlm;
extern bool feature_ntlm_wb;
extern bool feature_spnego;
tests/data/test3021+0 -4
@@ -17,10 +17,6 @@ test
# Client-side
<client>
-# so far only the libssh2 backend supports SHA256
-<features>
-libssh2
-</features>
<server>
sftp
</server>
tests/data/test3022+0 -4
@@ -17,10 +17,6 @@ test
# Client-side
<client>
-# so far only the libssh2 backend supports SHA256
-<features>
-libssh2
-</features>
<server>
scp
</server>