mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 09:58:01 +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:
parent
995e2f7164
commit
7cadad4cec
16
pageant.c
16
pageant.c
@ -2211,14 +2211,15 @@ int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
char *fingerprint = rsa_ssh1_fingerprint(&rkey);
|
char **fingerprints = rsa_ssh1_fake_all_fingerprints(&rkey);
|
||||||
freersakey(&rkey);
|
freersakey(&rkey);
|
||||||
|
|
||||||
callback(callback_ctx, fingerprint, cbkey.comment,
|
callback(callback_ctx, fingerprints, cbkey.comment,
|
||||||
kl1->keys[i].flags, &cbkey);
|
kl1->keys[i].flags, &cbkey);
|
||||||
|
|
||||||
strbuf_free(cbkey.blob);
|
strbuf_free(cbkey.blob);
|
||||||
sfree(cbkey.comment);
|
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.comment = mkstr(kl2->keys[i].comment);
|
||||||
cbkey.ssh_version = 2;
|
cbkey.ssh_version = 2;
|
||||||
|
|
||||||
char *fingerprint = ssh2_fingerprint_blob(kl2->keys[i].blob,
|
char **fingerprints =
|
||||||
SSH_FPTYPE_DEFAULT);
|
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);
|
kl2->keys[i].flags, &cbkey);
|
||||||
sfree(fingerprint);
|
|
||||||
|
ssh2_free_all_fingerprints(fingerprints);
|
||||||
sfree(cbkey.comment);
|
sfree(cbkey.comment);
|
||||||
strbuf_free(cbkey.blob);
|
strbuf_free(cbkey.blob);
|
||||||
}
|
}
|
||||||
|
@ -230,10 +230,8 @@ struct pageant_pubkey {
|
|||||||
struct pageant_pubkey *pageant_pubkey_copy(struct pageant_pubkey *key);
|
struct pageant_pubkey *pageant_pubkey_copy(struct pageant_pubkey *key);
|
||||||
void pageant_pubkey_free(struct pageant_pubkey *key);
|
void pageant_pubkey_free(struct pageant_pubkey *key);
|
||||||
|
|
||||||
typedef void (*pageant_key_enum_fn_t)(void *ctx,
|
typedef void (*pageant_key_enum_fn_t)(void *ctx, char **fingerprints,
|
||||||
const char *fingerprint,
|
const char *comment, uint32_t ext_flags,
|
||||||
const char *comment,
|
|
||||||
uint32_t ext_flags,
|
|
||||||
struct pageant_pubkey *key);
|
struct pageant_pubkey *key);
|
||||||
int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
|
int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
|
||||||
char **retstr);
|
char **retstr);
|
||||||
|
107
unix/uxpgnt.c
107
unix/uxpgnt.c
@ -450,6 +450,7 @@ static const char *display = NULL;
|
|||||||
static enum {
|
static enum {
|
||||||
PROMPT_UNSPEC, PROMPT_TTY, PROMPT_GUI
|
PROMPT_UNSPEC, PROMPT_TTY, PROMPT_GUI
|
||||||
} prompt_type = PROMPT_UNSPEC;
|
} prompt_type = PROMPT_UNSPEC;
|
||||||
|
static FingerprintType key_list_fptype = SSH_FPTYPE_DEFAULT;
|
||||||
|
|
||||||
static char *askpass_tty(const char *prompt)
|
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;
|
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)
|
uint32_t ext_flags, struct pageant_pubkey *key)
|
||||||
{
|
{
|
||||||
const char *mode = "";
|
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)
|
else if (ext_flags & LIST_EXTENDED_FLAG_HAS_ENCRYPTED_KEY_FILE)
|
||||||
mode = " (re-encryptable)";
|
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 {
|
struct key_find_ctx {
|
||||||
const char *string;
|
const char *string;
|
||||||
bool match_fp, match_comment;
|
bool match_fp, match_comment;
|
||||||
|
bool match_fptypes[SSH_N_FPTYPES];
|
||||||
struct pageant_pubkey *found;
|
struct pageant_pubkey *found;
|
||||||
int nfound;
|
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;
|
const char *hash;
|
||||||
|
|
||||||
/* Find the hash in the fingerprint string. It'll be the word at the end. */
|
for (unsigned fptype = 0; fptype < SSH_N_FPTYPES; fptype++) {
|
||||||
|
if (!ctx->match_fptypes[fptype])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
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, ' ');
|
hash = strrchr(fingerprint, ' ');
|
||||||
assert(hash);
|
assert(hash);
|
||||||
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,
|
/* Now see if the search string is a prefix of the full hash,
|
||||||
* neglecting colons and case differences. */
|
* neglecting colons and (where appropriate) case differences. */
|
||||||
while (1) {
|
while (1) {
|
||||||
while (*string == ':') string++;
|
string += strspn(string, ignore_chars);
|
||||||
while (*hash == ':') hash++;
|
hash += strspn(hash, ignore_chars);
|
||||||
if (!*string)
|
if (!*string)
|
||||||
return true;
|
return true;
|
||||||
if (tolower((unsigned char)*string) != tolower((unsigned char)*hash))
|
char sc = *string, hc = *hash;
|
||||||
return false;
|
if (!case_sensitive) {
|
||||||
|
sc = tolower((unsigned char)sc);
|
||||||
|
hc = tolower((unsigned char)hc);
|
||||||
|
}
|
||||||
|
if (sc != hc)
|
||||||
|
break;
|
||||||
string++;
|
string++;
|
||||||
hash++;
|
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,
|
const char *comment, uint32_t ext_flags,
|
||||||
struct pageant_pubkey *key)
|
struct pageant_pubkey *key)
|
||||||
{
|
{
|
||||||
struct key_find_ctx *ctx = (struct key_find_ctx *)vctx;
|
struct key_find_ctx *ctx = (struct key_find_ctx *)vctx;
|
||||||
|
|
||||||
if ((ctx->match_comment && !strcmp(ctx->string, comment)) ||
|
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)
|
if (!ctx->found)
|
||||||
ctx->found = pageant_pubkey_copy(key);
|
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;
|
struct pageant_pubkey key_in, *key_ret;
|
||||||
bool try_file = true, try_fp = true, try_comment = true;
|
bool try_file = true, try_fp = true, try_comment = true;
|
||||||
bool file_errors = false;
|
bool file_errors = false;
|
||||||
|
bool try_all_fptypes = true;
|
||||||
|
FingerprintType fptype = SSH_FPTYPE_DEFAULT;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Trim off disambiguating prefixes telling us how to interpret
|
* 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;
|
string += 12;
|
||||||
try_file = false;
|
try_file = false;
|
||||||
try_comment = 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->string = string;
|
||||||
ctx->match_fp = try_fp;
|
ctx->match_fp = try_fp;
|
||||||
ctx->match_comment = try_comment;
|
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->found = NULL;
|
||||||
ctx->nfound = 0;
|
ctx->nfound = 0;
|
||||||
if (pageant_enum_keys(key_find_callback, ctx, retstr) ==
|
if (pageant_enum_keys(key_find_callback, ctx, retstr) ==
|
||||||
@ -1327,6 +1394,24 @@ int main(int argc, char **argv)
|
|||||||
"after --symlink\n");
|
"after --symlink\n");
|
||||||
exit(1);
|
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, "--")) {
|
} else if (!strcmp(p, "--")) {
|
||||||
doing_opts = false;
|
doing_opts = false;
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user