unsigned char *keylist, *p;
int i, nkeys, keylistlen;
char *comment;
+ struct pageant_pubkey cbkey;
keylist = pageant_get_keylist1(&keylistlen);
if (keylistlen < 4) {
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);
}
return PAGEANT_ACTION_FAILURE;
}
fingerprint = fingerprint_ssh2_blob(p, n);
+ cbkey.blob = p;
+ cbkey.bloblen = n;
p += n, keylistlen -= n;
/* comment */
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);
}
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);
+}
#include <errno.h>
#include <assert.h>
#include <signal.h>
+#include <ctype.h>
#include <sys/types.h>
#include <sys/wait.h>
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;
}
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");