mirror of
https://github.com/curl/curl.git
synced 2026-08-24 15:23:36 +03:00
libssh2: add SHA256 fingerprint support
Added support for SHA256 fingerprint in command line curl and in libcurl. Closes #7646
This commit is contained in:
parent
1ca62bb5ce
commit
d1e7d9197b
27 changed files with 360 additions and 38 deletions
|
|
@ -271,6 +271,8 @@ struct curl_easyoption Curl_easyopts[] = {
|
|||
{"SSH_COMPRESSION", CURLOPT_SSH_COMPRESSION, CURLOT_LONG, 0},
|
||||
{"SSH_HOST_PUBLIC_KEY_MD5", CURLOPT_SSH_HOST_PUBLIC_KEY_MD5,
|
||||
CURLOT_STRING, 0},
|
||||
{"SSH_HOST_PUBLIC_KEY_SHA256", CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256,
|
||||
CURLOT_STRING, 0},
|
||||
{"SSH_KEYDATA", CURLOPT_SSH_KEYDATA, CURLOT_CBPTR, 0},
|
||||
{"SSH_KEYFUNCTION", CURLOPT_SSH_KEYFUNCTION, CURLOT_FUNCTION, 0},
|
||||
{"SSH_KNOWNHOSTS", CURLOPT_SSH_KNOWNHOSTS, CURLOT_STRING, 0},
|
||||
|
|
@ -354,6 +356,6 @@ struct curl_easyoption Curl_easyopts[] = {
|
|||
*/
|
||||
int Curl_easyopts_check(void)
|
||||
{
|
||||
return ((CURLOPT_LASTENTRY%10000) != (310 + 1));
|
||||
return ((CURLOPT_LASTENTRY%10000) != (311 + 1));
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -2477,6 +2477,15 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
|
|||
va_arg(param, char *));
|
||||
break;
|
||||
|
||||
case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256:
|
||||
/*
|
||||
* Option to allow for the SHA256 of the host public key to be checked
|
||||
* for validation purposes.
|
||||
*/
|
||||
result = Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256],
|
||||
va_arg(param, char *));
|
||||
break;
|
||||
|
||||
case CURLOPT_SSH_KNOWNHOSTS:
|
||||
/*
|
||||
* Store the file name to read known hosts from.
|
||||
|
|
|
|||
|
|
@ -1554,6 +1554,7 @@ enum dupstring {
|
|||
STRING_SSH_PRIVATE_KEY, /* path to the private key file for auth */
|
||||
STRING_SSH_PUBLIC_KEY, /* path to the public key file for auth */
|
||||
STRING_SSH_HOST_PUBLIC_KEY_MD5, /* md5 of host public key in ascii hex */
|
||||
STRING_SSH_HOST_PUBLIC_KEY_SHA256, /* sha256 of host public key in base64 */
|
||||
STRING_SSH_KNOWNHOSTS, /* file name of knownhosts file */
|
||||
STRING_PROXY_SERVICE_NAME, /* Proxy service name */
|
||||
STRING_SERVICE_NAME, /* Service name */
|
||||
|
|
|
|||
|
|
@ -81,6 +81,11 @@
|
|||
#include "select.h"
|
||||
#include "warnless.h"
|
||||
#include "curl_path.h"
|
||||
#include "strcase.h"
|
||||
|
||||
#include <curl_base64.h> /* for base64 encoding/decoding */
|
||||
#include <curl_sha256.h>
|
||||
|
||||
|
||||
/* The last 3 #include files should be in this order */
|
||||
#include "curl_printf.h"
|
||||
|
|
@ -615,40 +620,142 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
|
|||
struct connectdata *conn = data->conn;
|
||||
struct ssh_conn *sshc = &conn->proto.sshc;
|
||||
const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
|
||||
char md5buffer[33];
|
||||
const char *pubkey_sha256 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256];
|
||||
|
||||
const char *fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
|
||||
LIBSSH2_HOSTKEY_HASH_MD5);
|
||||
infof(data, "SSH MD5 public key: %s",
|
||||
pubkey_md5 != NULL ? pubkey_md5 : "NULL");
|
||||
infof(data, "SSH SHA256 public key: %s",
|
||||
pubkey_sha256 != NULL ? pubkey_sha256 : "NULL");
|
||||
|
||||
if(fingerprint) {
|
||||
if(pubkey_sha256) {
|
||||
const char *fingerprint = NULL;
|
||||
char *fingerprint_b64 = NULL;
|
||||
size_t fingerprint_b64_len;
|
||||
size_t pub_pos = 0;
|
||||
size_t b64_pos = 0;
|
||||
|
||||
#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
|
||||
/* The fingerprint points to static storage (!), don't free() it. */
|
||||
int i;
|
||||
for(i = 0; i < 16; i++)
|
||||
msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]);
|
||||
infof(data, "SSH MD5 fingerprint: %s", md5buffer);
|
||||
}
|
||||
fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
|
||||
LIBSSH2_HOSTKEY_HASH_SHA256);
|
||||
#else
|
||||
const char *hostkey;
|
||||
size_t len = 0;
|
||||
unsigned char hash[32];
|
||||
|
||||
/* Before we authenticate we check the hostkey's MD5 fingerprint
|
||||
* against a known fingerprint, if available.
|
||||
*/
|
||||
if(pubkey_md5 && strlen(pubkey_md5) == 32) {
|
||||
if(!fingerprint || !strcasecompare(md5buffer, pubkey_md5)) {
|
||||
if(fingerprint)
|
||||
failf(data,
|
||||
"Denied establishing ssh session: mismatch md5 fingerprint. "
|
||||
"Remote %s is not equal to %s", md5buffer, pubkey_md5);
|
||||
else
|
||||
failf(data,
|
||||
"Denied establishing ssh session: md5 fingerprint not available");
|
||||
hostkey = libssh2_session_hostkey(sshc->ssh_session, &len, NULL);
|
||||
if(hostkey) {
|
||||
Curl_sha256it(hash, (const unsigned char *) hostkey, len);
|
||||
fingerprint = (char *) hash;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!fingerprint) {
|
||||
failf(data,
|
||||
"Denied establishing ssh session: sha256 fingerprint "
|
||||
"not available");
|
||||
state(data, SSH_SESSION_FREE);
|
||||
sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
|
||||
return sshc->actualcode;
|
||||
}
|
||||
infof(data, "MD5 checksum match!");
|
||||
|
||||
/* The length of fingerprint is 32 bytes for SHA256.
|
||||
* See libssh2_hostkey_hash documentation. */
|
||||
if(Curl_base64_encode (data, fingerprint, 32, &fingerprint_b64,
|
||||
&fingerprint_b64_len) != CURLE_OK) {
|
||||
state(data, SSH_SESSION_FREE);
|
||||
sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
|
||||
return sshc->actualcode;
|
||||
}
|
||||
|
||||
if(!fingerprint_b64) {
|
||||
failf(data,
|
||||
"sha256 fingerprint could not be encoded");
|
||||
state(data, SSH_SESSION_FREE);
|
||||
sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
|
||||
return sshc->actualcode;
|
||||
}
|
||||
|
||||
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) ||
|
||||
Curl_strncasecompare(fingerprint_b64, pubkey_sha256, pub_pos) != 1) {
|
||||
free(fingerprint_b64);
|
||||
|
||||
failf(data,
|
||||
"Denied establishing ssh session: mismatch sha256 fingerprint. "
|
||||
"Remote %s is not equal to %s", fingerprint, pubkey_sha256);
|
||||
state(data, SSH_SESSION_FREE);
|
||||
sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
|
||||
return sshc->actualcode;
|
||||
}
|
||||
|
||||
free(fingerprint_b64);
|
||||
|
||||
infof(data, "SHA256 checksum match!");
|
||||
}
|
||||
|
||||
if(pubkey_md5) {
|
||||
char md5buffer[33];
|
||||
const char *fingerprint = NULL;
|
||||
|
||||
fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
|
||||
LIBSSH2_HOSTKEY_HASH_MD5);
|
||||
|
||||
if(fingerprint) {
|
||||
/* The fingerprint points to static storage (!), don't free() it. */
|
||||
int i;
|
||||
for(i = 0; i < 16; i++) {
|
||||
msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]);
|
||||
}
|
||||
|
||||
infof(data, "SSH MD5 fingerprint: %s", md5buffer);
|
||||
}
|
||||
|
||||
/* Before we authenticate we check the hostkey's MD5 fingerprint
|
||||
* against a known fingerprint, if available.
|
||||
*/
|
||||
if(pubkey_md5 && strlen(pubkey_md5) == 32) {
|
||||
if(!fingerprint || !strcasecompare(md5buffer, pubkey_md5)) {
|
||||
if(fingerprint) {
|
||||
failf(data,
|
||||
"Denied establishing ssh session: mismatch md5 fingerprint. "
|
||||
"Remote %s is not equal to %s", md5buffer, pubkey_md5);
|
||||
}
|
||||
else {
|
||||
failf(data,
|
||||
"Denied establishing ssh session: md5 fingerprint "
|
||||
"not available");
|
||||
}
|
||||
state(data, SSH_SESSION_FREE);
|
||||
sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
|
||||
return sshc->actualcode;
|
||||
}
|
||||
infof(data, "MD5 checksum match!");
|
||||
}
|
||||
}
|
||||
|
||||
if(!pubkey_md5 && !pubkey_sha256) {
|
||||
return ssh_knownhost(data);
|
||||
}
|
||||
else {
|
||||
/* as we already matched, we skip the check for known hosts */
|
||||
return CURLE_OK;
|
||||
}
|
||||
return ssh_knownhost(data);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue