mirror of
https://github.com/curl/curl.git
synced 2026-08-25 00:13:34 +03:00
libssh: add support for SHA256 host public keys
Reported-by: Joshua Rogers Fixes #21605 Closes #21607
This commit is contained in:
parent
9135294115
commit
eb9b253d66
11 changed files with 85 additions and 48 deletions
|
|
@ -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_MD5]) {
|
||||
int i;
|
||||
char md5buffer[33];
|
||||
const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
|
||||
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_MD5, &hash, &hlen);
|
||||
if(rc != SSH_OK || hlen != 16) {
|
||||
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]) {
|
||||
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_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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue