1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 09:27:59 +00:00

Allow pre-storing host key fingerprints of all types.

verify_ssh_manual_host_key() now takes an array of all key
fingerprints instead of just the default type, which means that an
expected key fingerprint stored in the session configuration can now
be matched against any of them.
This commit is contained in:
Simon Tatham 2021-03-13 10:53:53 +00:00
parent 46b23c581a
commit 04758cb3ec
5 changed files with 58 additions and 42 deletions

33
misc.c
View File

@ -22,6 +22,10 @@
#include "putty.h"
#include "misc.h"
#define BASE64_CHARS_NOEQ \
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/"
#define BASE64_CHARS_ALL BASE64_CHARS_NOEQ "="
void seat_connection_fatal(Seat *seat, const char *fmt, ...)
{
va_list ap;
@ -129,22 +133,32 @@ bool validate_manual_hostkey(char *key)
* Now q is our word.
*/
if (strlen(q) == 16*3 - 1 &&
q[strspn(q, "0123456789abcdefABCDEF:")] == 0) {
if (strstartswith(q, "SHA256:")) {
/* Test for a valid SHA256 key fingerprint. */
r = q + 7;
if (strlen(r) == 43 && r[strspn(r, BASE64_CHARS_NOEQ)] == 0)
return true;
}
r = q;
if (strstartswith(r, "MD5:"))
r += 4;
if (strlen(r) == 16*3 - 1 &&
r[strspn(r, "0123456789abcdefABCDEF:")] == 0) {
/*
* Might be a key fingerprint. Check the colons are in the
* right places, and if so, return the same fingerprint
* canonicalised into lowercase.
* Test for a valid MD5 key fingerprint. Check the colons
* are in the right places, and if so, return the same
* fingerprint canonicalised into lowercase.
*/
int i;
for (i = 0; i < 16; i++)
if (q[3*i] == ':' || q[3*i+1] == ':')
if (r[3*i] == ':' || r[3*i+1] == ':')
goto not_fingerprint; /* sorry */
for (i = 0; i < 15; i++)
if (q[3*i+2] != ':')
if (r[3*i+2] != ':')
goto not_fingerprint; /* sorry */
for (i = 0; i < 16*3 - 1; i++)
key[i] = tolower(q[i]);
key[i] = tolower(r[i]);
key[16*3 - 1] = '\0';
return true;
}
@ -161,8 +175,7 @@ bool validate_manual_hostkey(char *key)
*s = '\0';
if (strlen(q) % 4 == 0 && strlen(q) > 2*4 &&
q[strspn(q, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz+/=")] == 0) {
q[strspn(q, BASE64_CHARS_ALL)] == 0) {
/*
* Might be a base64-encoded SSH-2 public key blob. Check
* that it starts with a sensible algorithm string. No

3
ssh.h
View File

@ -1686,8 +1686,7 @@ unsigned alloc_channel_id_general(tree234 *channels, size_t localid_offset);
void add_to_commasep(strbuf *buf, const char *data);
bool get_commasep_word(ptrlen *list, ptrlen *word);
int verify_ssh_manual_host_key(
Conf *conf, const char *fingerprint, ssh_key *key);
int verify_ssh_manual_host_key(Conf *conf, char **fingerprints, ssh_key *key);
typedef struct ssh_transient_hostkey_cache ssh_transient_hostkey_cache;
ssh_transient_hostkey_cache *ssh_transient_hostkey_cache_new(void);

View File

@ -244,14 +244,13 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
/*
* First format the key into a string.
*/
char *fingerprint;
char *keystr = rsastr_fmt(&s->hostkey);
fingerprint = rsa_ssh1_fingerprint(&s->hostkey);
char **fingerprints = rsa_ssh1_fake_all_fingerprints(&s->hostkey);
/* First check against manually configured host keys. */
s->dlgret = verify_ssh_manual_host_key(s->conf, fingerprint, NULL);
s->dlgret = verify_ssh_manual_host_key(s->conf, fingerprints, NULL);
if (s->dlgret == 0) { /* did not match */
sfree(fingerprint);
ssh2_free_all_fingerprints(fingerprints);
sfree(keystr);
ssh_proto_error(s->ppl.ssh, "Host key did not appear in manually "
"configured list");
@ -259,8 +258,9 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
} else if (s->dlgret < 0) { /* none configured; use standard handling */
s->dlgret = seat_verify_ssh_host_key(
s->ppl.seat, s->savedhost, s->savedport,
"rsa", keystr, fingerprint, ssh1_login_dialog_callback, s);
sfree(fingerprint);
"rsa", keystr, fingerprints[SSH_FPTYPE_DEFAULT],
ssh1_login_dialog_callback, s);
ssh2_free_all_fingerprints(fingerprints);
sfree(keystr);
#ifdef FUZZING
s->dlgret = 1;
@ -273,7 +273,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
return;
}
} else {
sfree(fingerprint);
ssh2_free_all_fingerprints(fingerprints);
sfree(keystr);
}
}

View File

@ -843,13 +843,16 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
* Authenticate remote host: verify host key. (We've already
* checked the signature of the exchange hash.)
*/
char *fingerprint = ssh2_fingerprint(s->hkey, SSH_FPTYPE_DEFAULT);
char **fingerprints = ssh2_all_fingerprints(s->hkey);
FingerprintType fptype_default =
ssh2_pick_default_fingerprint(fingerprints);
ppl_logevent("Host key fingerprint is:");
ppl_logevent("%s", fingerprint);
ppl_logevent("%s", fingerprints[fptype_default]);
/* First check against manually configured host keys. */
s->dlgret = verify_ssh_manual_host_key(
s->conf, fingerprint, s->hkey);
s->conf, fingerprints, s->hkey);
if (s->dlgret == 0) { /* did not match */
ssh2_free_all_fingerprints(fingerprints);
ssh_sw_abort(s->ppl.ssh, "Host key did not appear in manually "
"configured list");
*aborted = true;
@ -857,9 +860,10 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
} else if (s->dlgret < 0) { /* none configured; use standard handling */
s->dlgret = seat_verify_ssh_host_key(
s->ppl.seat, s->savedhost, s->savedport,
ssh_key_cache_id(s->hkey), s->keystr, fingerprint,
ssh_key_cache_id(s->hkey), s->keystr,
fingerprints[SSH_FPTYPE_DEFAULT],
ssh2_transport_dialog_callback, s);
sfree(fingerprint);
ssh2_free_all_fingerprints(fingerprints);
#ifdef FUZZING
s->dlgret = 1;
#endif

View File

@ -826,28 +826,28 @@ bool ssh2_bpp_check_unimplemented(BinaryPacketProtocol *bpp, PktIn *pktin)
* Function to check a host key against any manually configured in Conf.
*/
int verify_ssh_manual_host_key(
Conf *conf, const char *fingerprint, ssh_key *key)
int verify_ssh_manual_host_key(Conf *conf, char **fingerprints, ssh_key *key)
{
if (!conf_get_str_nthstrkey(conf, CONF_ssh_manual_hostkeys, 0))
return -1; /* no manual keys configured */
if (fingerprint) {
/*
* The fingerprint string we've been given will have things
* like 'ssh-rsa 2048' at the front of it. Strip those off and
* narrow down to just the colon-separated hex block at the
* end of the string.
*/
const char *p = strrchr(fingerprint, ' ');
fingerprint = p ? p+1 : fingerprint;
/* Quick sanity checks, including making sure it's in lowercase */
assert(strlen(fingerprint) == 16*3 - 1);
assert(fingerprint[2] == ':');
assert(fingerprint[strspn(fingerprint, "0123456789abcdef:")] == 0);
if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys, fingerprint))
return 1; /* success */
if (fingerprints) {
for (size_t i = 0; i < SSH_N_FPTYPES; i++) {
/*
* Each fingerprint string we've been given will have
* things like 'ssh-rsa 2048' at the front of it. Strip
* those off and narrow down to just the hash at the end
* of the string.
*/
const char *fingerprint = fingerprints[i];
if (!fingerprint)
continue;
const char *p = strrchr(fingerprint, ' ');
fingerprint = p ? p+1 : fingerprint;
if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys,
fingerprint))
return 1; /* success */
}
}
if (key) {