X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=pageant.c;h=e7430e7865faac93a173f15ede19442d76ea54cf;hb=b03020cab9297c53d1a65a497910ea7a988b94e7;hp=f6383a3c9403a1ba3ac08375a81461225d45997b;hpb=47c9a6ef0bda6bed52f1c37ff4f8ef98734d349a;p=PuTTY.git diff --git a/pageant.c b/pageant.c index f6383a3c..e7430e78 100644 --- a/pageant.c +++ b/pageant.c @@ -27,6 +27,8 @@ int random_byte(void) return 0; /* unreachable, but placate optimiser */ } +static int pageant_local = FALSE; + /* * rsakeys stores SSH-1 RSA keys. ssh2keys stores all SSH-2 keys. */ @@ -257,25 +259,6 @@ void *pageant_make_keylist2(int *length) return ret; } -char *fingerprint_ssh2_blob(const void *blob, int bloblen) -{ - unsigned char digest[16]; - char fingerprint_str[16*3]; - unsigned stringlen; - int i; - - MD5Simple(blob, bloblen, digest); - for (i = 0; i < 16; i++) - sprintf(fingerprint_str + i*3, "%02x%s", digest[i], i==15 ? "" : ":"); - - stringlen = GET_32BIT((const unsigned char *)blob); - if (stringlen < bloblen-4) - return dupprintf("%.*s %s", (int)stringlen, (const char *)blob + 4, - fingerprint_str); - else - return dupstr(fingerprint_str); -} - static void plog(void *logctx, pageant_logfn_t logfn, const char *fmt, ...) #ifdef __GNUC__ __attribute__ ((format (printf, 3, 4))) @@ -379,7 +362,8 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen, int i; struct ssh2_userkey *skey; for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) { - char *fingerprint = skey->alg->fingerprint(skey->data); + char *fingerprint = ssh2_fingerprint(skey->alg, + skey->data); plog(logctx, logfn, "returned key: %s %s", fingerprint, skey->comment); sfree(fingerprint); @@ -422,6 +406,7 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen, if (i < 0) { freebn(reqkey.exponent); freebn(reqkey.modulus); + freebn(challenge); fail_reason = "request truncated before challenge"; goto failure; } @@ -526,7 +511,7 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen, } data = p; if (logfn) { - char *fingerprint = fingerprint_ssh2_blob(b.blob, b.len); + char *fingerprint = ssh2_fingerprint_blob(b.blob, b.len); plog(logctx, logfn, "requested key: %s", fingerprint); sfree(fingerprint); } @@ -682,25 +667,15 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen, p += alglen; key = snew(struct ssh2_userkey); - /* Add further algorithm names here. */ - if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7)) - key->alg = &ssh_rsa; - else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7)) - key->alg = &ssh_dss; - else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp256", 19)) - key->alg = &ssh_ecdsa_nistp256; - else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp384", 19)) - key->alg = &ssh_ecdsa_nistp384; - else if (alglen == 19 && memcmp(alg, "ecdsa-sha2-nistp521", 19)) - key->alg = &ssh_ecdsa_nistp521; - else { + key->alg = find_pubkey_alg_len(alglen, alg); + if (!key->alg) { sfree(key); fail_reason = "algorithm unknown"; goto failure; } bloblen = msgend - p; - key->data = key->alg->openssh_createkey(&p, &bloblen); + key->data = key->alg->openssh_createkey(key->alg, &p, &bloblen); if (!key->data) { sfree(key); fail_reason = "key setup failed"; @@ -736,7 +711,7 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen, key->comment = comment; if (logfn) { - char *fingerprint = key->alg->fingerprint(key->data); + char *fingerprint = ssh2_fingerprint(key->alg, key->data); plog(logctx, logfn, "submitted key: %s %s", fingerprint, key->comment); sfree(fingerprint); @@ -830,7 +805,7 @@ void *pageant_handle_msg(const void *msg, int msglen, int *outlen, p += b.len; if (logfn) { - char *fingerprint = fingerprint_ssh2_blob(b.blob, b.len); + char *fingerprint = ssh2_fingerprint_blob(b.blob, b.len); plog(logctx, logfn, "unwanted key: %s", fingerprint); sfree(fingerprint); } @@ -928,6 +903,7 @@ void *pageant_failure_msg(int *outlen) void pageant_init(void) { + pageant_local = TRUE; rsakeys = newtree234(cmpkeys_rsa); ssh2keys = newtree234(cmpkeys_ssh2); } @@ -1125,6 +1101,7 @@ static int pageant_listen_accepting(Plug plug, struct pageant_listen_state *pl = (struct pageant_listen_state *)plug; struct pageant_conn_state *pc; const char *err; + char *peerinfo; pc = snew(struct pageant_conn_state); pc->fn = &connection_fn_table; @@ -1141,8 +1118,13 @@ static int pageant_listen_accepting(Plug plug, sk_set_frozen(pc->connsock, 0); - /* FIXME: can we get any useful peer id info? */ - plog(pl->logctx, pl->logfn, "%p: new connection", pc); + peerinfo = sk_peer_info(pc->connsock); + if (peerinfo) { + plog(pl->logctx, pl->logfn, "%p: new connection from %s", + pc, peerinfo); + } else { + plog(pl->logctx, pl->logfn, "%p: new connection", pc); + } return 0; } @@ -1183,3 +1165,667 @@ void pageant_listener_free(struct pageant_listen_state *pl) sk_close(pl->listensock); sfree(pl); } + +/* ---------------------------------------------------------------------- + * Code to perform agent operations either as a client, or within the + * same process as the running agent. + */ + +static tree234 *passphrases = NULL; + +/* + * After processing a list of filenames, we want to forget the + * passphrases. + */ +void pageant_forget_passphrases(void) +{ + if (!passphrases) /* in case we never set it up at all */ + return; + + while (count234(passphrases) > 0) { + char *pp = index234(passphrases, 0); + smemclr(pp, strlen(pp)); + delpos234(passphrases, 0); + free(pp); + } +} + +void *pageant_get_keylist1(int *length) +{ + void *ret; + + if (!pageant_local) { + unsigned char request[5], *response; + void *vresponse; + int resplen; + + request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES; + PUT_32BIT(request, 1); + + agent_query_synchronous(request, 5, &vresponse, &resplen); + response = vresponse; + if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) { + sfree(response); + return NULL; + } + + ret = snewn(resplen-5, unsigned char); + memcpy(ret, response+5, resplen-5); + sfree(response); + + if (length) + *length = resplen-5; + } else { + ret = pageant_make_keylist1(length); + } + return ret; +} + +void *pageant_get_keylist2(int *length) +{ + void *ret; + + if (!pageant_local) { + unsigned char request[5], *response; + void *vresponse; + int resplen; + + request[4] = SSH2_AGENTC_REQUEST_IDENTITIES; + PUT_32BIT(request, 1); + + agent_query_synchronous(request, 5, &vresponse, &resplen); + response = vresponse; + if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) { + sfree(response); + return NULL; + } + + ret = snewn(resplen-5, unsigned char); + memcpy(ret, response+5, resplen-5); + sfree(response); + + if (length) + *length = resplen-5; + } else { + ret = pageant_make_keylist2(length); + } + return ret; +} + +int pageant_add_keyfile(Filename *filename, const char *passphrase, + char **retstr) +{ + struct RSAKey *rkey = NULL; + struct ssh2_userkey *skey = NULL; + int needs_pass; + int ret; + int attempts; + char *comment; + const char *this_passphrase; + const char *error = NULL; + int type; + + if (!passphrases) { + passphrases = newtree234(NULL); + } + + *retstr = NULL; + + type = key_type(filename); + if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) { + *retstr = dupprintf("Couldn't load this key (%s)", + key_type_to_str(type)); + return PAGEANT_ACTION_FAILURE; + } + + /* + * See if the key is already loaded (in the primary Pageant, + * which may or may not be us). + */ + { + void *blob; + unsigned char *keylist, *p; + int i, nkeys, bloblen, keylistlen; + + if (type == SSH_KEYTYPE_SSH1) { + if (!rsakey_pubblob(filename, &blob, &bloblen, NULL, &error)) { + *retstr = dupprintf("Couldn't load private key (%s)", error); + return PAGEANT_ACTION_FAILURE; + } + keylist = pageant_get_keylist1(&keylistlen); + } else { + unsigned char *blob2; + blob = ssh2_userkey_loadpub(filename, NULL, &bloblen, + NULL, &error); + if (!blob) { + *retstr = dupprintf("Couldn't load private key (%s)", error); + return PAGEANT_ACTION_FAILURE; + } + /* For our purposes we want the blob prefixed with its length */ + blob2 = snewn(bloblen+4, unsigned char); + PUT_32BIT(blob2, bloblen); + memcpy(blob2 + 4, blob, bloblen); + sfree(blob); + blob = blob2; + + keylist = pageant_get_keylist2(&keylistlen); + } + if (keylist) { + if (keylistlen < 4) { + *retstr = dupstr("Received broken key list from agent"); + return PAGEANT_ACTION_FAILURE; + } + nkeys = toint(GET_32BIT(keylist)); + if (nkeys < 0) { + *retstr = dupstr("Received broken key list from agent"); + return PAGEANT_ACTION_FAILURE; + } + p = keylist + 4; + keylistlen -= 4; + + for (i = 0; i < nkeys; i++) { + if (!memcmp(blob, p, bloblen)) { + /* Key is already present; we can now leave. */ + sfree(keylist); + sfree(blob); + return PAGEANT_ACTION_OK; + } + /* Now skip over public blob */ + if (type == SSH_KEYTYPE_SSH1) { + int n = rsa_public_blob_len(p, keylistlen); + if (n < 0) { + *retstr = dupstr("Received broken key list from agent"); + sfree(keylist); + sfree(blob); + return PAGEANT_ACTION_FAILURE; + } + p += n; + keylistlen -= n; + } else { + int n; + if (keylistlen < 4) { + *retstr = dupstr("Received broken key list from agent"); + sfree(keylist); + sfree(blob); + return PAGEANT_ACTION_FAILURE; + } + n = GET_32BIT(p); + p += 4; + keylistlen -= 4; + + if (n < 0 || n > keylistlen) { + *retstr = dupstr("Received broken key list from agent"); + sfree(keylist); + sfree(blob); + return PAGEANT_ACTION_FAILURE; + } + p += n; + keylistlen -= n; + } + /* Now skip over comment field */ + { + int n; + if (keylistlen < 4) { + *retstr = dupstr("Received broken key list from agent"); + sfree(keylist); + sfree(blob); + return PAGEANT_ACTION_FAILURE; + } + n = GET_32BIT(p); + p += 4; + keylistlen -= 4; + + if (n < 0 || n > keylistlen) { + *retstr = dupstr("Received broken key list from agent"); + sfree(keylist); + sfree(blob); + return PAGEANT_ACTION_FAILURE; + } + p += n; + keylistlen -= n; + } + } + + sfree(keylist); + } + + sfree(blob); + } + + error = NULL; + if (type == SSH_KEYTYPE_SSH1) + needs_pass = rsakey_encrypted(filename, &comment); + else + needs_pass = ssh2_userkey_encrypted(filename, &comment); + attempts = 0; + if (type == SSH_KEYTYPE_SSH1) + rkey = snew(struct RSAKey); + + /* + * Loop round repeatedly trying to load the key, until we either + * succeed, fail for some serious reason, or run out of + * passphrases to try. + */ + while (1) { + if (needs_pass) { + + /* + * If we've been given a passphrase on input, try using + * it. Otherwise, try one from our tree234 of previously + * useful passphrases. + */ + if (passphrase) { + this_passphrase = (attempts == 0 ? passphrase : NULL); + } else { + this_passphrase = (const char *)index234(passphrases, attempts); + } + + if (!this_passphrase) { + /* + * Run out of passphrases to try. + */ + *retstr = comment; + sfree(rkey); + return PAGEANT_ACTION_NEED_PP; + } + } else + this_passphrase = ""; + + if (type == SSH_KEYTYPE_SSH1) + ret = loadrsakey(filename, rkey, this_passphrase, &error); + else { + skey = ssh2_load_userkey(filename, this_passphrase, &error); + if (skey == SSH2_WRONG_PASSPHRASE) + ret = -1; + else if (!skey) + ret = 0; + else + ret = 1; + } + + if (ret == 0) { + /* + * Failed to load the key file, for some reason other than + * a bad passphrase. + */ + *retstr = dupstr(error); + sfree(rkey); + return PAGEANT_ACTION_FAILURE; + } else if (ret == 1) { + /* + * Successfully loaded the key file. + */ + break; + } else { + /* + * Passphrase wasn't right; go round again. + */ + attempts++; + } + } + + /* + * If we get here, we've succesfully loaded the key into + * rkey/skey, but not yet added it to the agent. + */ + + /* + * If the key was successfully decrypted, save the passphrase for + * use with other keys we try to load. + */ + { + char *pp_copy = dupstr(this_passphrase); + if (addpos234(passphrases, pp_copy, 0) != pp_copy) { + /* No need; it was already there. */ + smemclr(pp_copy, strlen(pp_copy)); + sfree(pp_copy); + } + } + + if (comment) + sfree(comment); + + if (type == SSH_KEYTYPE_SSH1) { + if (!pageant_local) { + unsigned char *request, *response; + void *vresponse; + int reqlen, clen, resplen; + + clen = strlen(rkey->comment); + + reqlen = 4 + 1 + /* length, message type */ + 4 + /* bit count */ + ssh1_bignum_length(rkey->modulus) + + ssh1_bignum_length(rkey->exponent) + + ssh1_bignum_length(rkey->private_exponent) + + ssh1_bignum_length(rkey->iqmp) + + ssh1_bignum_length(rkey->p) + + ssh1_bignum_length(rkey->q) + 4 + clen /* comment */ + ; + + request = snewn(reqlen, unsigned char); + + request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY; + reqlen = 5; + PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus)); + reqlen += 4; + reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus); + reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent); + reqlen += + ssh1_write_bignum(request + reqlen, + rkey->private_exponent); + reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp); + reqlen += ssh1_write_bignum(request + reqlen, rkey->p); + reqlen += ssh1_write_bignum(request + reqlen, rkey->q); + PUT_32BIT(request + reqlen, clen); + memcpy(request + reqlen + 4, rkey->comment, clen); + reqlen += 4 + clen; + PUT_32BIT(request, reqlen - 4); + + agent_query_synchronous(request, reqlen, &vresponse, &resplen); + response = vresponse; + if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) { + *retstr = dupstr("The already running Pageant " + "refused to add the key."); + freersakey(rkey); + sfree(rkey); + sfree(request); + sfree(response); + return PAGEANT_ACTION_FAILURE; + } + freersakey(rkey); + sfree(rkey); + sfree(request); + sfree(response); + } else { + if (!pageant_add_ssh1_key(rkey)) { + freersakey(rkey); + sfree(rkey); /* already present, don't waste RAM */ + } + } + } else { + if (!pageant_local) { + unsigned char *request, *response; + void *vresponse; + int reqlen, alglen, clen, keybloblen, resplen; + alglen = strlen(skey->alg->name); + clen = strlen(skey->comment); + + keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0); + + reqlen = 4 + 1 + /* length, message type */ + 4 + alglen + /* algorithm name */ + keybloblen + /* key data */ + 4 + clen /* comment */ + ; + + request = snewn(reqlen, unsigned char); + + request[4] = SSH2_AGENTC_ADD_IDENTITY; + reqlen = 5; + PUT_32BIT(request + reqlen, alglen); + reqlen += 4; + memcpy(request + reqlen, skey->alg->name, alglen); + reqlen += alglen; + reqlen += skey->alg->openssh_fmtkey(skey->data, + request + reqlen, + keybloblen); + PUT_32BIT(request + reqlen, clen); + memcpy(request + reqlen + 4, skey->comment, clen); + reqlen += clen + 4; + PUT_32BIT(request, reqlen - 4); + + agent_query_synchronous(request, reqlen, &vresponse, &resplen); + response = vresponse; + if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) { + *retstr = dupstr("The already running Pageant " + "refused to add the key."); + sfree(request); + sfree(response); + return PAGEANT_ACTION_FAILURE; + } + + sfree(request); + sfree(response); + } else { + if (!pageant_add_ssh2_key(skey)) { + skey->alg->freekey(skey->data); + sfree(skey); /* already present, don't waste RAM */ + } + } + } + return PAGEANT_ACTION_OK; +} + +int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx, + char **retstr) +{ + unsigned char *keylist, *p; + int i, nkeys, keylistlen; + char *comment; + struct pageant_pubkey cbkey; + + keylist = pageant_get_keylist1(&keylistlen); + if (keylistlen < 4) { + *retstr = dupstr("Received broken SSH-1 key list from agent"); + sfree(keylist); + return PAGEANT_ACTION_FAILURE; + } + nkeys = toint(GET_32BIT(keylist)); + if (nkeys < 0) { + *retstr = dupstr("Received broken SSH-1 key list from agent"); + sfree(keylist); + return PAGEANT_ACTION_FAILURE; + } + p = keylist + 4; + keylistlen -= 4; + + for (i = 0; i < nkeys; i++) { + struct RSAKey rkey; + char fingerprint[128]; + int n; + + /* public blob and fingerprint */ + memset(&rkey, 0, sizeof(rkey)); + n = makekey(p, keylistlen, &rkey, NULL, 0); + if (n < 0 || n > keylistlen) { + freersakey(&rkey); + *retstr = dupstr("Received broken SSH-1 key list from agent"); + sfree(keylist); + return PAGEANT_ACTION_FAILURE; + } + p += n, keylistlen -= n; + rsa_fingerprint(fingerprint, sizeof(fingerprint), &rkey); + + /* comment */ + if (keylistlen < 4) { + *retstr = dupstr("Received broken SSH-1 key list from agent"); + freersakey(&rkey); + sfree(keylist); + return PAGEANT_ACTION_FAILURE; + } + n = toint(GET_32BIT(p)); + p += 4, keylistlen -= 4; + if (n < 0 || keylistlen < n) { + *retstr = dupstr("Received broken SSH-1 key list from agent"); + freersakey(&rkey); + sfree(keylist); + return PAGEANT_ACTION_FAILURE; + } + comment = dupprintf("%.*s", (int)n, (const char *)p); + p += n, keylistlen -= n; + + cbkey.blob = rsa_public_blob(&rkey, &cbkey.bloblen); + cbkey.comment = comment; + cbkey.ssh_version = 1; + callback(callback_ctx, fingerprint, comment, &cbkey); + sfree(cbkey.blob); + freersakey(&rkey); + sfree(comment); + } + + sfree(keylist); + + if (keylistlen != 0) { + *retstr = dupstr("Received broken SSH-1 key list from agent"); + return PAGEANT_ACTION_FAILURE; + } + + keylist = pageant_get_keylist2(&keylistlen); + if (keylistlen < 4) { + *retstr = dupstr("Received broken SSH-2 key list from agent"); + sfree(keylist); + return PAGEANT_ACTION_FAILURE; + } + nkeys = toint(GET_32BIT(keylist)); + if (nkeys < 0) { + *retstr = dupstr("Received broken SSH-2 key list from agent"); + sfree(keylist); + return PAGEANT_ACTION_FAILURE; + } + p = keylist + 4; + keylistlen -= 4; + + for (i = 0; i < nkeys; i++) { + char *fingerprint; + int n; + + /* public blob */ + if (keylistlen < 4) { + *retstr = dupstr("Received broken SSH-2 key list from agent"); + sfree(keylist); + return PAGEANT_ACTION_FAILURE; + } + n = toint(GET_32BIT(p)); + p += 4, keylistlen -= 4; + if (n < 0 || keylistlen < n) { + *retstr = dupstr("Received broken SSH-2 key list from agent"); + sfree(keylist); + return PAGEANT_ACTION_FAILURE; + } + fingerprint = ssh2_fingerprint_blob(p, n); + cbkey.blob = p; + cbkey.bloblen = n; + p += n, keylistlen -= n; + + /* comment */ + if (keylistlen < 4) { + *retstr = dupstr("Received broken SSH-2 key list from agent"); + sfree(fingerprint); + sfree(keylist); + return PAGEANT_ACTION_FAILURE; + } + n = toint(GET_32BIT(p)); + p += 4, keylistlen -= 4; + if (n < 0 || keylistlen < n) { + *retstr = dupstr("Received broken SSH-2 key list from agent"); + sfree(fingerprint); + sfree(keylist); + return PAGEANT_ACTION_FAILURE; + } + comment = dupprintf("%.*s", (int)n, (const char *)p); + p += n, keylistlen -= n; + + cbkey.ssh_version = 2; + cbkey.comment = comment; + callback(callback_ctx, fingerprint, comment, &cbkey); + sfree(fingerprint); + sfree(comment); + } + + sfree(keylist); + + if (keylistlen != 0) { + *retstr = dupstr("Received broken SSH-1 key list from agent"); + return PAGEANT_ACTION_FAILURE; + } + + 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); + } + + agent_query_synchronous(request, reqlen, &vresponse, &resplen); + 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; +} + +int pageant_delete_all_keys(char **retstr) +{ + unsigned char request[5], *response; + int reqlen, resplen, success; + void *vresponse; + + PUT_32BIT(request, 1); + request[4] = SSH2_AGENTC_REMOVE_ALL_IDENTITIES; + reqlen = 5; + agent_query_synchronous(request, reqlen, &vresponse, &resplen); + response = vresponse; + success = (resplen >= 4 && response[4] == SSH_AGENT_SUCCESS); + sfree(response); + if (!success) { + *retstr = dupstr("Agent failed to delete SSH-2 keys"); + return PAGEANT_ACTION_FAILURE; + } + + PUT_32BIT(request, 1); + request[4] = SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES; + reqlen = 5; + agent_query_synchronous(request, reqlen, &vresponse, &resplen); + response = vresponse; + success = (resplen >= 4 && response[4] == SSH_AGENT_SUCCESS); + sfree(response); + if (!success) { + *retstr = dupstr("Agent failed to delete SSH-1 keys"); + return PAGEANT_ACTION_FAILURE; + } + + *retstr = NULL; + return PAGEANT_ACTION_OK; +} + +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->comment = key->comment ? dupstr(key->comment) : NULL; + ret->ssh_version = key->ssh_version; + return ret; +} + +void pageant_pubkey_free(struct pageant_pubkey *key) +{ + sfree(key->comment); + sfree(key->blob); + sfree(key); +}