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

Unix Pageant: support multiple fingerprint types.

The callback-function API in pageant.h for key enumeration is modified
so that we pass an array of all the available fingerprints for each
key.

In Unix Pageant, that's used by the -l option to print whichever
fingerprint the user asked for. (Unfortunately, the option name -E is
already taken, so for the moment I've called it --fptype. I may
revisit that later.)

Also, when matching a key by fingerprint, we're prepared to match
against any fingerprint type we know, with disambiguating prefixes if
necessary (e.g. you can match "md5🆎12" or "sha256:Ab12". That has
to be done a bit carefully, because we match MD5 hex fingerprints
case-insensitively, but SHA256 fingerprints are case-sensitive.
This commit is contained in:
Simon Tatham 2021-03-13 10:27:50 +00:00
parent 995e2f7164
commit 7cadad4cec
3 changed files with 115 additions and 30 deletions

View File

@ -2211,14 +2211,15 @@ int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
goto out;
}
}
char *fingerprint = rsa_ssh1_fingerprint(&rkey);
char **fingerprints = rsa_ssh1_fake_all_fingerprints(&rkey);
freersakey(&rkey);
callback(callback_ctx, fingerprint, cbkey.comment,
callback(callback_ctx, fingerprints, cbkey.comment,
kl1->keys[i].flags, &cbkey);
strbuf_free(cbkey.blob);
sfree(cbkey.comment);
sfree(fingerprint);
ssh2_free_all_fingerprints(fingerprints);
}
}
@ -2229,12 +2230,13 @@ int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
cbkey.comment = mkstr(kl2->keys[i].comment);
cbkey.ssh_version = 2;
char *fingerprint = ssh2_fingerprint_blob(kl2->keys[i].blob,
SSH_FPTYPE_DEFAULT);
char **fingerprints =
ssh2_all_fingerprints_for_blob(kl2->keys[i].blob);
callback(callback_ctx, fingerprint, cbkey.comment,
callback(callback_ctx, fingerprints, cbkey.comment,
kl2->keys[i].flags, &cbkey);
sfree(fingerprint);
ssh2_free_all_fingerprints(fingerprints);
sfree(cbkey.comment);
strbuf_free(cbkey.blob);
}

View File

@ -230,10 +230,8 @@ struct pageant_pubkey {
struct pageant_pubkey *pageant_pubkey_copy(struct pageant_pubkey *key);
void pageant_pubkey_free(struct pageant_pubkey *key);
typedef void (*pageant_key_enum_fn_t)(void *ctx,
const char *fingerprint,
const char *comment,
uint32_t ext_flags,
typedef void (*pageant_key_enum_fn_t)(void *ctx, char **fingerprints,
const char *comment, uint32_t ext_flags,
struct pageant_pubkey *key);
int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
char **retstr);

View File

@ -450,6 +450,7 @@ static const char *display = NULL;
static enum {
PROMPT_UNSPEC, PROMPT_TTY, PROMPT_GUI
} prompt_type = PROMPT_UNSPEC;
static FingerprintType key_list_fptype = SSH_FPTYPE_DEFAULT;
static char *askpass_tty(const char *prompt)
{
@ -576,7 +577,7 @@ static bool unix_add_keyfile(const char *filename_str, bool add_encrypted)
return ret;
}
void key_list_callback(void *ctx, const char *fingerprint, const char *comment,
void key_list_callback(void *ctx, char **fingerprints, const char *comment,
uint32_t ext_flags, struct pageant_pubkey *key)
{
const char *mode = "";
@ -585,47 +586,97 @@ void key_list_callback(void *ctx, const char *fingerprint, const char *comment,
else if (ext_flags & LIST_EXTENDED_FLAG_HAS_ENCRYPTED_KEY_FILE)
mode = " (re-encryptable)";
printf("%s %s%s\n", fingerprint, comment, mode);
FingerprintType this_type =
ssh2_pick_fingerprint(fingerprints, key_list_fptype);
printf("%s %s%s\n", fingerprints[this_type], comment, mode);
}
struct key_find_ctx {
const char *string;
bool match_fp, match_comment;
bool match_fptypes[SSH_N_FPTYPES];
struct pageant_pubkey *found;
int nfound;
};
bool match_fingerprint_string(const char *string, const char *fingerprint)
static bool match_fingerprint_string(
const char *string_orig, char **fingerprints,
const struct key_find_ctx *ctx)
{
const char *hash;
/* Find the hash in the fingerprint string. It'll be the word at the end. */
hash = strrchr(fingerprint, ' ');
assert(hash);
hash++;
for (unsigned fptype = 0; fptype < SSH_N_FPTYPES; fptype++) {
if (!ctx->match_fptypes[fptype])
continue;
/* Now see if the search string is a prefix of the full hash,
* neglecting colons and case differences. */
while (1) {
while (*string == ':') string++;
while (*hash == ':') hash++;
if (!*string)
return true;
if (tolower((unsigned char)*string) != tolower((unsigned char)*hash))
return false;
string++;
const char *fingerprint = fingerprints[fptype];
if (!fingerprint)
continue;
/* Find the hash in the fingerprint string. It'll be the word
* at the end. */
hash = strrchr(fingerprint, ' ');
assert(hash);
hash++;
const char *string = string_orig;
bool case_sensitive;
const char *ignore_chars = "";
switch (fptype) {
case SSH_FPTYPE_MD5:
/* MD5 fingerprints are in hex, so disregard case differences. */
case_sensitive = false;
/* And we don't really need to force the user to type the
* colons in between the digits, which are always the
* same. */
ignore_chars = ":";
break;
case SSH_FPTYPE_SHA256:
/* Skip over the "SHA256:" prefix, which we don't really
* want to force the user to type. On the other hand,
* tolerate it on the input string. */
assert(strstartswith(hash, "SHA256:"));
hash += 7;
if (strstartswith(string, "SHA256:"))
string += 7;
/* SHA256 fingerprints are base64, which is intrinsically
* case sensitive. */
case_sensitive = true;
break;
}
/* Now see if the search string is a prefix of the full hash,
* neglecting colons and (where appropriate) case differences. */
while (1) {
string += strspn(string, ignore_chars);
hash += strspn(hash, ignore_chars);
if (!*string)
return true;
char sc = *string, hc = *hash;
if (!case_sensitive) {
sc = tolower((unsigned char)sc);
hc = tolower((unsigned char)hc);
}
if (sc != hc)
break;
string++;
hash++;
}
}
return false;
}
void key_find_callback(void *vctx, const char *fingerprint,
void key_find_callback(void *vctx, char **fingerprints,
const char *comment, uint32_t ext_flags,
struct pageant_pubkey *key)
{
struct key_find_ctx *ctx = (struct key_find_ctx *)vctx;
if ((ctx->match_comment && !strcmp(ctx->string, comment)) ||
(ctx->match_fp && match_fingerprint_string(ctx->string, fingerprint)))
(ctx->match_fp && match_fingerprint_string(ctx->string, fingerprints,
ctx)))
{
if (!ctx->found)
ctx->found = pageant_pubkey_copy(key);
@ -639,6 +690,8 @@ struct pageant_pubkey *find_key(const char *string, char **retstr)
struct pageant_pubkey key_in, *key_ret;
bool try_file = true, try_fp = true, try_comment = true;
bool file_errors = false;
bool try_all_fptypes = true;
FingerprintType fptype = SSH_FPTYPE_DEFAULT;
/*
* Trim off disambiguating prefixes telling us how to interpret
@ -661,6 +714,18 @@ struct pageant_pubkey *find_key(const char *string, char **retstr)
string += 12;
try_file = false;
try_comment = false;
} else if (!strnicmp(string, "md5:", 4)) {
string += 4;
try_file = false;
try_comment = false;
try_all_fptypes = false;
fptype = SSH_FPTYPE_MD5;
} else if (!strncmp(string, "sha256:", 7)) {
string += 7;
try_file = false;
try_comment = false;
try_all_fptypes = false;
fptype = SSH_FPTYPE_SHA256;
}
/*
@ -746,6 +811,8 @@ struct pageant_pubkey *find_key(const char *string, char **retstr)
ctx->string = string;
ctx->match_fp = try_fp;
ctx->match_comment = try_comment;
for (unsigned i = 0; i < SSH_N_FPTYPES; i++)
ctx->match_fptypes[i] = (try_all_fptypes || i == fptype);
ctx->found = NULL;
ctx->nfound = 0;
if (pageant_enum_keys(key_find_callback, ctx, retstr) ==
@ -1327,6 +1394,24 @@ int main(int argc, char **argv)
"after --symlink\n");
exit(1);
}
} else if (!strcmp(p, "--fptype")) {
const char *keyword;
if (--argc > 0) {
keyword = *++argv;
} else {
fprintf(stderr, "pageant: expected a pathname "
"after --fptype\n");
exit(1);
}
if (!strcmp(keyword, "md5"))
key_list_fptype = SSH_FPTYPE_MD5;
else if (!strcmp(keyword, "sha256"))
key_list_fptype = SSH_FPTYPE_SHA256;
else {
fprintf(stderr, "pageant: unknown fingerprint type `%s'\n",
keyword);
exit(1);
}
} else if (!strcmp(p, "--")) {
doing_opts = false;
} else {