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:
parent
4204a53f6d
commit
4d88fe3dde
63
pageant.c
63
pageant.c
@ -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);
|
||||
}
|
||||
|
16
pageant.h
16
pageant.h
@ -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);
|
||||
|
181
unix/uxpgnt.c
181
unix/uxpgnt.c
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user