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

Unix Pageant: support -d, to delete a key from the agent.

Unlike ssh-add, we can identify the key by its comment or by a prefix
of its fingerprint as well as using a public key file on disk. The
string given as an argument to -d is interpreted as whichever of those
things matches; disambiguating prefixes are available if needed.
This commit is contained in:
Simon Tatham 2015-05-12 13:27:33 +01:00
parent 4204a53f6d
commit 4d88fe3dde
3 changed files with 256 additions and 4 deletions

View File

@ -1589,6 +1589,7 @@ int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
unsigned char *keylist, *p;
int i, nkeys, keylistlen;
char *comment;
struct pageant_pubkey cbkey;
keylist = pageant_get_keylist1(&keylistlen);
if (keylistlen < 4) {
@ -1640,7 +1641,10 @@ int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
comment = dupprintf("%.*s", (int)n, (const char *)p);
p += n, keylistlen -= n;
callback(callback_ctx, fingerprint, comment);
cbkey.blob = rsa_public_blob(&rkey, &cbkey.bloblen);
cbkey.ssh_version = 1;
callback(callback_ctx, fingerprint, comment, &cbkey);
sfree(cbkey.blob);
freersakey(&rkey);
sfree(comment);
}
@ -1685,6 +1689,8 @@ int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
return PAGEANT_ACTION_FAILURE;
}
fingerprint = fingerprint_ssh2_blob(p, n);
cbkey.blob = p;
cbkey.bloblen = n;
p += n, keylistlen -= n;
/* comment */
@ -1705,7 +1711,8 @@ int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
comment = dupprintf("%.*s", (int)n, (const char *)p);
p += n, keylistlen -= n;
callback(callback_ctx, fingerprint, comment);
cbkey.ssh_version = 2;
callback(callback_ctx, fingerprint, comment, &cbkey);
sfree(fingerprint);
sfree(comment);
}
@ -1719,3 +1726,55 @@ int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
return PAGEANT_ACTION_OK;
}
int pageant_delete_key(struct pageant_pubkey *key, char **retstr)
{
unsigned char *request, *response;
int reqlen, resplen, ret;
void *vresponse;
if (key->ssh_version == 1) {
reqlen = 5 + key->bloblen;
request = snewn(reqlen, unsigned char);
PUT_32BIT(request, reqlen - 4);
request[4] = SSH1_AGENTC_REMOVE_RSA_IDENTITY;
memcpy(request + 5, key->blob, key->bloblen);
} else {
reqlen = 9 + key->bloblen;
request = snewn(reqlen, unsigned char);
PUT_32BIT(request, reqlen - 4);
request[4] = SSH2_AGENTC_REMOVE_IDENTITY;
PUT_32BIT(request + 5, key->bloblen);
memcpy(request + 9, key->blob, key->bloblen);
}
ret = agent_query(request, reqlen, &vresponse, &resplen, NULL, NULL);
assert(ret == 1);
response = vresponse;
if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) {
*retstr = dupstr("Agent failed to delete key");
ret = PAGEANT_ACTION_FAILURE;
} else {
*retstr = NULL;
ret = PAGEANT_ACTION_OK;
}
sfree(request);
sfree(response);
return ret;
}
struct pageant_pubkey *pageant_pubkey_copy(struct pageant_pubkey *key)
{
struct pageant_pubkey *ret = snew(struct pageant_pubkey);
ret->blob = snewn(key->bloblen, unsigned char);
memcpy(ret->blob, key->blob, key->bloblen);
ret->bloblen = key->bloblen;
ret->ssh_version = key->ssh_version;
return ret;
}
void pageant_pubkey_free(struct pageant_pubkey *key)
{
sfree(key->blob);
sfree(key);
}

View File

@ -120,8 +120,22 @@ enum {
int pageant_add_keyfile(Filename *filename, const char *passphrase,
char **retstr);
void pageant_forget_passphrases(void);
struct pageant_pubkey {
/* Everything needed to identify a public key found by
* pageant_enum_keys and pass it back to the agent or other code
* later */
void *blob;
int bloblen;
int ssh_version;
};
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);
const char *comment,
struct pageant_pubkey *key);
int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx,
char **retstr);
int pageant_delete_key(struct pageant_pubkey *key, char **retstr);

View File

@ -7,6 +7,7 @@
#include <errno.h>
#include <assert.h>
#include <signal.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/wait.h>
@ -354,14 +355,181 @@ static int unix_add_keyfile(const char *filename_str)
return ret;
}
void key_list_callback(void *ctx, const char *fingerprint, const char *comment)
void key_list_callback(void *ctx, const char *fingerprint,
const char *comment, struct pageant_pubkey *key)
{
printf("%s %s\n", fingerprint, comment);
}
struct key_find_ctx {
const char *string;
int match_fp, match_comment;
struct pageant_pubkey *found;
int nfound;
};
int match_fingerprint_string(const char *string, const char *fingerprint)
{
const char *hash;
/* Find the hash in the fingerprint string. It'll be the word at the end. */
hash = strrchr(fingerprint, ' ');
assert(hash);
hash++;
/* 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++;
hash++;
}
}
void key_find_callback(void *vctx, const char *fingerprint,
const char *comment, 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)))
{
if (!ctx->found)
ctx->found = pageant_pubkey_copy(key);
ctx->nfound++;
}
}
struct pageant_pubkey *find_key(const char *string, char **retstr)
{
struct key_find_ctx actx, *ctx = &actx;
struct pageant_pubkey key_in, *key_ret;
int try_file = TRUE, try_fp = TRUE, try_comment = TRUE;
int file_errors = FALSE;
/*
* Trim off disambiguating prefixes telling us how to interpret
* the provided string.
*/
if (!strncmp(string, "file:", 5)) {
string += 5;
try_fp = try_comment = FALSE;
file_errors = TRUE; /* also report failure to load the file */
} else if (!strncmp(string, "comment:", 8)) {
string += 8;
try_file = try_fp = FALSE;
} else if (!strncmp(string, "fp:", 3)) {
string += 3;
try_file = try_comment = FALSE;
} else if (!strncmp(string, "fingerprint:", 12)) {
string += 12;
try_file = try_comment = FALSE;
}
/*
* Try interpreting the string as a key file name.
*/
if (try_file) {
Filename *fn = filename_from_str(string);
int keytype = key_type(fn);
if (keytype == SSH_KEYTYPE_SSH1 ||
keytype == SSH_KEYTYPE_SSH1_PUBLIC) {
const char *error;
if (!rsakey_pubblob(fn, &key_in.blob, &key_in.bloblen,
NULL, &error)) {
if (file_errors) {
*retstr = dupprintf("unable to load file '%s': %s",
string, error);
filename_free(fn);
return NULL;
}
}
/*
* If we've successfully loaded the file, stop here - we
* already have a key blob and need not go to the agent to
* list things.
*/
key_in.ssh_version = 1;
key_ret = pageant_pubkey_copy(&key_in);
sfree(key_in.blob);
filename_free(fn);
return key_ret;
} else if (keytype == SSH_KEYTYPE_SSH2 ||
keytype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 ||
keytype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) {
const char *error;
if ((key_in.blob = ssh2_userkey_loadpub(fn, NULL,
&key_in.bloblen,
NULL, &error)) == NULL) {
if (file_errors) {
*retstr = dupprintf("unable to load file '%s': %s",
string, error);
filename_free(fn);
return NULL;
}
}
/*
* If we've successfully loaded the file, stop here - we
* already have a key blob and need not go to the agent to
* list things.
*/
key_in.ssh_version = 2;
key_ret = pageant_pubkey_copy(&key_in);
sfree(key_in.blob);
filename_free(fn);
return key_ret;
} else {
if (file_errors) {
*retstr = dupprintf("unable to load key file '%s': %s",
string, key_type_to_str(keytype));
filename_free(fn);
return NULL;
}
}
filename_free(fn);
}
/*
* Failing that, go through the keys in the agent, and match
* against fingerprints and comments as appropriate.
*/
ctx->string = string;
ctx->match_fp = try_fp;
ctx->match_comment = try_comment;
ctx->found = NULL;
ctx->nfound = 0;
if (pageant_enum_keys(key_find_callback, ctx, retstr) ==
PAGEANT_ACTION_FAILURE)
return NULL;
if (ctx->nfound == 0) {
*retstr = dupstr("no key matched");
assert(!ctx->found);
return NULL;
} else if (ctx->nfound > 1) {
*retstr = dupstr("multiple keys matched");
assert(ctx->found);
pageant_pubkey_free(ctx->found);
return NULL;
}
assert(ctx->found);
return ctx->found;
}
void run_client(void)
{
const struct cmdline_key_action *act;
struct pageant_pubkey *key;
int errors = FALSE;
char *retstr;
@ -385,6 +553,17 @@ void run_client(void)
}
break;
case KEYACT_CLIENT_DEL:
key = NULL;
if (!(key = find_key(act->filename, &retstr)) ||
pageant_delete_key(key, &retstr) == PAGEANT_ACTION_FAILURE) {
fprintf(stderr, "pageant: deleting key '%s': %s\n",
act->filename, retstr);
sfree(retstr);
errors = TRUE;
}
if (key)
pageant_pubkey_free(key);
break;
case KEYACT_CLIENT_DEL_ALL:
case KEYACT_CLIENT_LIST_FULL:
fprintf(stderr, "NYI\n");