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:
parent
46b23c581a
commit
04758cb3ec
33
misc.c
33
misc.c
@ -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
3
ssh.h
@ -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);
|
||||
|
14
ssh1login.c
14
ssh1login.c
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
36
sshcommon.c
36
sshcommon.c
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user