2015-05-05 22:16:19 +03:00
|
|
|
/*
|
|
|
|
* pageant.c: cross-platform code to implement Pageant.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include "putty.h"
|
|
|
|
#include "ssh.h"
|
|
|
|
#include "pageant.h"
|
|
|
|
|
|
|
|
/*
|
2018-05-24 10:22:44 +03:00
|
|
|
* We need this to link with the RSA code, because rsa_ssh1_encrypt()
|
|
|
|
* pads its data with random bytes. Since we only use rsa_ssh1_decrypt()
|
2015-05-05 22:16:19 +03:00
|
|
|
* and the signing functions, which are deterministic, this should
|
|
|
|
* never be called.
|
|
|
|
*
|
|
|
|
* If it _is_ called, there is a _serious_ problem, because it
|
|
|
|
* won't generate true random numbers. So we must scream, panic,
|
|
|
|
* and exit immediately if that should happen.
|
|
|
|
*/
|
|
|
|
int random_byte(void)
|
|
|
|
{
|
|
|
|
modalfatalbox("Internal error: attempt to use random numbers in Pageant");
|
|
|
|
exit(0);
|
|
|
|
return 0; /* unreachable, but placate optimiser */
|
|
|
|
}
|
|
|
|
|
2015-05-11 17:06:25 +03:00
|
|
|
static int pageant_local = FALSE;
|
|
|
|
|
2015-05-05 22:16:19 +03:00
|
|
|
/*
|
|
|
|
* rsakeys stores SSH-1 RSA keys. ssh2keys stores all SSH-2 keys.
|
|
|
|
*/
|
|
|
|
static tree234 *rsakeys, *ssh2keys;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Key comparison function for the 2-3-4 tree of RSA keys.
|
|
|
|
*/
|
|
|
|
static int cmpkeys_rsa(void *av, void *bv)
|
|
|
|
{
|
|
|
|
struct RSAKey *a = (struct RSAKey *) av;
|
|
|
|
struct RSAKey *b = (struct RSAKey *) bv;
|
|
|
|
Bignum am, bm;
|
|
|
|
int alen, blen;
|
|
|
|
|
|
|
|
am = a->modulus;
|
|
|
|
bm = b->modulus;
|
|
|
|
/*
|
|
|
|
* Compare by length of moduli.
|
|
|
|
*/
|
|
|
|
alen = bignum_bitcount(am);
|
|
|
|
blen = bignum_bitcount(bm);
|
|
|
|
if (alen > blen)
|
|
|
|
return +1;
|
|
|
|
else if (alen < blen)
|
|
|
|
return -1;
|
|
|
|
/*
|
|
|
|
* Now compare by moduli themselves.
|
|
|
|
*/
|
|
|
|
alen = (alen + 7) / 8; /* byte count */
|
|
|
|
while (alen-- > 0) {
|
|
|
|
int abyte, bbyte;
|
|
|
|
abyte = bignum_byte(am, alen);
|
|
|
|
bbyte = bignum_byte(bm, alen);
|
|
|
|
if (abyte > bbyte)
|
|
|
|
return +1;
|
|
|
|
else if (abyte < bbyte)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Give up.
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-05-24 12:59:39 +03:00
|
|
|
* Key comparison function for looking up a blob in the 2-3-4 tree
|
|
|
|
* of SSH-2 keys.
|
2015-05-05 22:16:19 +03:00
|
|
|
*/
|
2018-05-24 12:59:39 +03:00
|
|
|
static int cmpkeys_ssh2_asymm(void *av, void *bv)
|
2015-05-05 22:16:19 +03:00
|
|
|
{
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf *ablob = (strbuf *) av;
|
2015-05-05 22:16:19 +03:00
|
|
|
struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf *bblob;
|
|
|
|
int i, c;
|
2015-05-05 22:16:19 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Compare purely by public blob.
|
|
|
|
*/
|
2018-05-24 12:59:39 +03:00
|
|
|
bblob = strbuf_new();
|
|
|
|
b->alg->public_blob(b->data, BinarySink_UPCAST(bblob));
|
2015-05-05 22:16:19 +03:00
|
|
|
|
|
|
|
c = 0;
|
2018-05-24 12:59:39 +03:00
|
|
|
for (i = 0; i < ablob->len && i < bblob->len; i++) {
|
|
|
|
if (ablob->u[i] < bblob->u[i]) {
|
2015-05-05 22:16:19 +03:00
|
|
|
c = -1;
|
|
|
|
break;
|
2018-05-24 12:59:39 +03:00
|
|
|
} else if (ablob->u[i] > bblob->u[i]) {
|
2015-05-05 22:16:19 +03:00
|
|
|
c = +1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-05-24 12:59:39 +03:00
|
|
|
if (c == 0 && i < ablob->len)
|
2015-05-05 22:16:19 +03:00
|
|
|
c = +1; /* a is longer */
|
2018-05-24 12:59:39 +03:00
|
|
|
if (c == 0 && i < bblob->len)
|
2015-05-05 22:16:19 +03:00
|
|
|
c = -1; /* a is longer */
|
|
|
|
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf_free(bblob);
|
2015-05-05 22:16:19 +03:00
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-05-24 12:59:39 +03:00
|
|
|
* Main key comparison function for the 2-3-4 tree of SSH-2 keys.
|
2015-05-05 22:16:19 +03:00
|
|
|
*/
|
2018-05-24 12:59:39 +03:00
|
|
|
static int cmpkeys_ssh2(void *av, void *bv)
|
2015-05-05 22:16:19 +03:00
|
|
|
{
|
2018-05-24 12:59:39 +03:00
|
|
|
struct ssh2_userkey *a = (struct ssh2_userkey *) av;
|
|
|
|
strbuf *ablob;
|
|
|
|
int toret;
|
|
|
|
|
|
|
|
ablob = strbuf_new();
|
|
|
|
a->alg->public_blob(a->data, BinarySink_UPCAST(ablob));
|
|
|
|
toret = cmpkeys_ssh2_asymm(ablob, bv);
|
|
|
|
strbuf_free(ablob);
|
|
|
|
return toret;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
|
2018-05-24 15:23:17 +03:00
|
|
|
void pageant_make_keylist1(BinarySink *bs)
|
2015-05-05 22:16:19 +03:00
|
|
|
{
|
2018-05-24 12:59:39 +03:00
|
|
|
int i;
|
2015-05-05 22:16:19 +03:00
|
|
|
struct RSAKey *key;
|
|
|
|
|
2018-05-24 12:59:39 +03:00
|
|
|
put_uint32(bs, count234(rsakeys));
|
2015-05-05 22:16:19 +03:00
|
|
|
for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
|
2018-05-24 12:59:39 +03:00
|
|
|
rsa_ssh1_public_blob(bs, key, RSA_SSH1_EXPONENT_FIRST);
|
|
|
|
put_stringz(bs, key->comment);
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-24 15:23:17 +03:00
|
|
|
void pageant_make_keylist2(BinarySink *bs)
|
2015-05-05 22:16:19 +03:00
|
|
|
{
|
2018-05-24 12:59:39 +03:00
|
|
|
int i;
|
2015-05-05 22:16:19 +03:00
|
|
|
struct ssh2_userkey *key;
|
|
|
|
|
2018-05-24 12:59:39 +03:00
|
|
|
put_uint32(bs, count234(ssh2keys));
|
2015-05-05 22:16:19 +03:00
|
|
|
for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf *blob = strbuf_new();
|
|
|
|
key->alg->public_blob(key->data, BinarySink_UPCAST(blob));
|
|
|
|
put_stringsb(bs, blob);
|
|
|
|
put_stringz(bs, key->comment);
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-06 21:32:26 +03:00
|
|
|
static void plog(void *logctx, pageant_logfn_t logfn, const char *fmt, ...)
|
|
|
|
#ifdef __GNUC__
|
|
|
|
__attribute__ ((format (printf, 3, 4)))
|
|
|
|
#endif
|
|
|
|
;
|
|
|
|
|
|
|
|
static void plog(void *logctx, pageant_logfn_t logfn, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* This is the wrapper that takes a variadic argument list and
|
|
|
|
* turns it into the va_list that the log function really expects.
|
|
|
|
* It's safe to call this with logfn==NULL, because we
|
|
|
|
* double-check that below; but if you're going to do lots of work
|
|
|
|
* before getting here (such as looping, or hashing things) then
|
|
|
|
* you should probably check logfn manually before doing that.
|
|
|
|
*/
|
|
|
|
if (logfn) {
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
logfn(logctx, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-24 15:23:17 +03:00
|
|
|
void pageant_handle_msg(BinarySink *bs,
|
|
|
|
const void *msg, int msglen,
|
|
|
|
void *logctx, pageant_logfn_t logfn)
|
2015-05-05 22:16:19 +03:00
|
|
|
{
|
|
|
|
const unsigned char *p = msg;
|
|
|
|
const unsigned char *msgend;
|
|
|
|
int type;
|
|
|
|
|
|
|
|
msgend = p + msglen;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the message type.
|
|
|
|
*/
|
2015-05-06 21:32:26 +03:00
|
|
|
if (msgend < p+1) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(bs, "message contained no type code",
|
|
|
|
logctx, logfn);
|
|
|
|
return;
|
2015-05-06 21:32:26 +03:00
|
|
|
}
|
2015-05-05 22:16:19 +03:00
|
|
|
type = *p++;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
|
|
|
|
/*
|
|
|
|
* Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
|
|
|
|
*/
|
|
|
|
{
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(logctx, logfn, "request: SSH1_AGENTC_REQUEST_RSA_IDENTITIES");
|
|
|
|
|
2018-05-24 15:23:17 +03:00
|
|
|
put_byte(bs, SSH1_AGENT_RSA_IDENTITIES_ANSWER);
|
|
|
|
pageant_make_keylist1(bs);
|
2015-05-06 21:32:26 +03:00
|
|
|
|
|
|
|
plog(logctx, logfn, "reply: SSH1_AGENT_RSA_IDENTITIES_ANSWER");
|
|
|
|
if (logfn) { /* skip this loop if not logging */
|
|
|
|
int i;
|
|
|
|
struct RSAKey *rkey;
|
|
|
|
for (i = 0; NULL != (rkey = pageant_nth_ssh1_key(i)); i++) {
|
|
|
|
char fingerprint[128];
|
|
|
|
rsa_fingerprint(fingerprint, sizeof(fingerprint), rkey);
|
|
|
|
plog(logctx, logfn, "returned key: %s", fingerprint);
|
|
|
|
}
|
|
|
|
}
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SSH2_AGENTC_REQUEST_IDENTITIES:
|
|
|
|
/*
|
|
|
|
* Reply with SSH2_AGENT_IDENTITIES_ANSWER.
|
|
|
|
*/
|
|
|
|
{
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(logctx, logfn, "request: SSH2_AGENTC_REQUEST_IDENTITIES");
|
|
|
|
|
2018-05-24 15:23:17 +03:00
|
|
|
put_byte(bs, SSH2_AGENT_IDENTITIES_ANSWER);
|
|
|
|
pageant_make_keylist2(bs);
|
2015-05-06 21:32:26 +03:00
|
|
|
|
|
|
|
plog(logctx, logfn, "reply: SSH2_AGENT_IDENTITIES_ANSWER");
|
|
|
|
if (logfn) { /* skip this loop if not logging */
|
|
|
|
int i;
|
|
|
|
struct ssh2_userkey *skey;
|
|
|
|
for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) {
|
2015-05-12 16:35:44 +03:00
|
|
|
char *fingerprint = ssh2_fingerprint(skey->alg,
|
|
|
|
skey->data);
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(logctx, logfn, "returned key: %s %s",
|
|
|
|
fingerprint, skey->comment);
|
|
|
|
sfree(fingerprint);
|
|
|
|
}
|
|
|
|
}
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SSH1_AGENTC_RSA_CHALLENGE:
|
|
|
|
/*
|
|
|
|
* Reply with either SSH1_AGENT_RSA_RESPONSE or
|
|
|
|
* SSH_AGENT_FAILURE, depending on whether we have that key
|
|
|
|
* or not.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
struct RSAKey reqkey, *key;
|
|
|
|
Bignum challenge, response;
|
|
|
|
unsigned char response_source[48], response_md5[16];
|
|
|
|
struct MD5Context md5c;
|
2018-05-24 15:23:17 +03:00
|
|
|
int i;
|
2015-05-05 22:16:19 +03:00
|
|
|
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(logctx, logfn, "request: SSH1_AGENTC_RSA_CHALLENGE");
|
|
|
|
|
2018-05-24 15:23:17 +03:00
|
|
|
reqkey.exponent = reqkey.modulus = challenge = response = NULL;
|
|
|
|
|
2015-05-05 22:16:19 +03:00
|
|
|
p += 4;
|
|
|
|
i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
|
2015-05-06 21:32:26 +03:00
|
|
|
if (i < 0) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before key exponent",
|
|
|
|
logctx, logfn);
|
|
|
|
goto challenge1_cleanup;
|
2015-05-06 21:32:26 +03:00
|
|
|
}
|
2015-05-05 22:16:19 +03:00
|
|
|
p += i;
|
|
|
|
i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
|
|
|
|
if (i < 0) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before key modulus",
|
|
|
|
logctx, logfn);
|
|
|
|
goto challenge1_cleanup;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
p += i;
|
|
|
|
i = ssh1_read_bignum(p, msgend - p, &challenge);
|
|
|
|
if (i < 0) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before challenge",
|
|
|
|
logctx, logfn);
|
|
|
|
goto challenge1_cleanup;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
p += i;
|
|
|
|
if (msgend < p+16) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before session id",
|
|
|
|
logctx, logfn);
|
|
|
|
goto challenge1_cleanup;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
memcpy(response_source + 32, p, 16);
|
|
|
|
p += 16;
|
2015-05-06 21:32:26 +03:00
|
|
|
if (msgend < p+4) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before response type",
|
|
|
|
logctx, logfn);
|
|
|
|
goto challenge1_cleanup;
|
2015-05-06 21:32:26 +03:00
|
|
|
}
|
|
|
|
if (GET_32BIT(p) != 1) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "response type other than 1 not supported",
|
|
|
|
logctx, logfn);
|
|
|
|
goto challenge1_cleanup;
|
2015-05-06 21:32:26 +03:00
|
|
|
}
|
|
|
|
if (logfn) {
|
|
|
|
char fingerprint[128];
|
|
|
|
reqkey.comment = NULL;
|
|
|
|
rsa_fingerprint(fingerprint, sizeof(fingerprint), &reqkey);
|
|
|
|
plog(logctx, logfn, "requested key: %s", fingerprint);
|
|
|
|
}
|
|
|
|
if ((key = find234(rsakeys, &reqkey, NULL)) == NULL) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(bs, "key not found", logctx, logfn);
|
|
|
|
goto challenge1_cleanup;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
2018-05-24 10:22:44 +03:00
|
|
|
response = rsa_ssh1_decrypt(challenge, key);
|
2015-05-05 22:16:19 +03:00
|
|
|
for (i = 0; i < 32; i++)
|
|
|
|
response_source[i] = bignum_byte(response, 31 - i);
|
|
|
|
|
|
|
|
MD5Init(&md5c);
|
2018-05-24 12:03:36 +03:00
|
|
|
put_data(&md5c, response_source, 48);
|
2015-05-05 22:16:19 +03:00
|
|
|
MD5Final(response_md5, &md5c);
|
|
|
|
smemclr(response_source, 48); /* burn the evidence */
|
|
|
|
|
2018-05-24 15:23:17 +03:00
|
|
|
put_byte(bs, SSH1_AGENT_RSA_RESPONSE);
|
|
|
|
put_data(bs, response_md5, 16);
|
2015-05-06 21:32:26 +03:00
|
|
|
|
|
|
|
plog(logctx, logfn, "reply: SSH1_AGENT_RSA_RESPONSE");
|
2018-05-24 15:23:17 +03:00
|
|
|
|
|
|
|
challenge1_cleanup:
|
|
|
|
if (response) freebn(response);
|
|
|
|
if (challenge) freebn(challenge);
|
|
|
|
if (reqkey.exponent) freebn(reqkey.exponent);
|
|
|
|
if (reqkey.modulus) freebn(reqkey.modulus);
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SSH2_AGENTC_SIGN_REQUEST:
|
|
|
|
/*
|
|
|
|
* Reply with either SSH2_AGENT_SIGN_RESPONSE or
|
|
|
|
* SSH_AGENT_FAILURE, depending on whether we have that key
|
|
|
|
* or not.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
struct ssh2_userkey *key;
|
2018-05-24 12:59:39 +03:00
|
|
|
const void *blobp;
|
|
|
|
int bloblen;
|
2015-05-05 22:16:19 +03:00
|
|
|
const unsigned char *data;
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf *signature;
|
2018-05-24 15:23:17 +03:00
|
|
|
int datalen;
|
2015-05-05 22:16:19 +03:00
|
|
|
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(logctx, logfn, "request: SSH2_AGENTC_SIGN_REQUEST");
|
|
|
|
|
|
|
|
if (msgend < p+4) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before public key",
|
|
|
|
logctx, logfn);
|
|
|
|
return;
|
2015-05-06 21:32:26 +03:00
|
|
|
}
|
2018-05-24 12:59:39 +03:00
|
|
|
bloblen = toint(GET_32BIT(p));
|
|
|
|
if (bloblen < 0 || bloblen > msgend - (p+4)) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before public key",
|
|
|
|
logctx, logfn);
|
|
|
|
return;
|
2015-05-06 21:32:26 +03:00
|
|
|
}
|
2015-05-05 22:16:19 +03:00
|
|
|
p += 4;
|
2018-05-24 12:59:39 +03:00
|
|
|
blobp = p;
|
|
|
|
p += bloblen;
|
2015-05-06 21:32:26 +03:00
|
|
|
if (msgend < p+4) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before string to sign",
|
|
|
|
logctx, logfn);
|
|
|
|
return;
|
2015-05-06 21:32:26 +03:00
|
|
|
}
|
2015-05-05 22:16:19 +03:00
|
|
|
datalen = toint(GET_32BIT(p));
|
|
|
|
p += 4;
|
2015-05-06 21:32:26 +03:00
|
|
|
if (datalen < 0 || datalen > msgend - p) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before string to sign",
|
|
|
|
logctx, logfn);
|
|
|
|
return;
|
2015-05-06 21:32:26 +03:00
|
|
|
}
|
2015-05-05 22:16:19 +03:00
|
|
|
data = p;
|
2015-05-06 21:32:26 +03:00
|
|
|
if (logfn) {
|
2018-05-24 12:59:39 +03:00
|
|
|
char *fingerprint = ssh2_fingerprint_blob(blobp, bloblen);
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(logctx, logfn, "requested key: %s", fingerprint);
|
|
|
|
sfree(fingerprint);
|
|
|
|
}
|
2018-05-24 12:59:39 +03:00
|
|
|
{
|
|
|
|
strbuf *blob = strbuf_new();
|
|
|
|
put_data(blob, blobp, bloblen);
|
|
|
|
key = find234(ssh2keys, blob, cmpkeys_ssh2_asymm);
|
|
|
|
strbuf_free(blob);
|
|
|
|
}
|
2015-05-06 21:32:26 +03:00
|
|
|
if (!key) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(bs, "key not found", logctx, logfn);
|
|
|
|
return;
|
2015-05-06 21:32:26 +03:00
|
|
|
}
|
2018-05-24 15:23:17 +03:00
|
|
|
|
|
|
|
put_byte(bs, SSH2_AGENT_SIGN_RESPONSE);
|
|
|
|
|
2018-05-24 12:59:39 +03:00
|
|
|
signature = strbuf_new();
|
2018-05-26 10:31:34 +03:00
|
|
|
key->alg->sign(key->data, data, datalen,
|
2018-05-24 12:59:39 +03:00
|
|
|
BinarySink_UPCAST(signature));
|
2018-05-24 15:23:17 +03:00
|
|
|
put_stringsb(bs, signature);
|
2015-05-06 21:32:26 +03:00
|
|
|
|
|
|
|
plog(logctx, logfn, "reply: SSH2_AGENT_SIGN_RESPONSE");
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SSH1_AGENTC_ADD_RSA_IDENTITY:
|
|
|
|
/*
|
|
|
|
* Add to the list and return SSH_AGENT_SUCCESS, or
|
|
|
|
* SSH_AGENT_FAILURE if the key was malformed.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
struct RSAKey *key;
|
|
|
|
char *comment;
|
|
|
|
int n, commentlen;
|
|
|
|
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(logctx, logfn, "request: SSH1_AGENTC_ADD_RSA_IDENTITY");
|
|
|
|
|
2015-05-05 22:16:19 +03:00
|
|
|
key = snew(struct RSAKey);
|
|
|
|
memset(key, 0, sizeof(struct RSAKey));
|
|
|
|
|
2018-05-24 10:22:44 +03:00
|
|
|
n = rsa_ssh1_readpub(p, msgend - p, key, NULL,
|
|
|
|
RSA_SSH1_MODULUS_FIRST);
|
2015-05-05 22:16:19 +03:00
|
|
|
if (n < 0) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before public key",
|
|
|
|
logctx, logfn);
|
|
|
|
goto add1_cleanup;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
p += n;
|
|
|
|
|
2018-05-24 10:22:44 +03:00
|
|
|
n = rsa_ssh1_readpriv(p, msgend - p, key);
|
2015-05-05 22:16:19 +03:00
|
|
|
if (n < 0) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before private key",
|
|
|
|
logctx, logfn);
|
|
|
|
goto add1_cleanup;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
p += n;
|
|
|
|
|
2015-05-06 22:49:07 +03:00
|
|
|
/* SSH-1 names p and q the other way round, i.e. we have
|
|
|
|
* the inverse of p mod q and not of q mod p. We swap the
|
|
|
|
* names, because our internal RSA wants iqmp. */
|
|
|
|
|
2015-05-05 22:16:19 +03:00
|
|
|
n = ssh1_read_bignum(p, msgend - p, &key->iqmp); /* p^-1 mod q */
|
|
|
|
if (n < 0) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(bs, "request truncated before iqmp",
|
|
|
|
logctx, logfn);
|
|
|
|
goto add1_cleanup;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
p += n;
|
|
|
|
|
2015-05-06 22:49:07 +03:00
|
|
|
n = ssh1_read_bignum(p, msgend - p, &key->q); /* p */
|
2015-05-05 22:16:19 +03:00
|
|
|
if (n < 0) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(bs, "request truncated before p",
|
|
|
|
logctx, logfn);
|
|
|
|
goto add1_cleanup;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
p += n;
|
|
|
|
|
2015-05-06 22:49:07 +03:00
|
|
|
n = ssh1_read_bignum(p, msgend - p, &key->p); /* q */
|
2015-05-05 22:16:19 +03:00
|
|
|
if (n < 0) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before q", logctx, logfn);
|
|
|
|
goto add1_cleanup;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
p += n;
|
|
|
|
|
|
|
|
if (msgend < p+4) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before key comment",
|
|
|
|
logctx, logfn);
|
|
|
|
goto add1_cleanup;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
commentlen = toint(GET_32BIT(p));
|
|
|
|
|
|
|
|
if (commentlen < 0 || commentlen > msgend - p) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before key comment",
|
|
|
|
logctx, logfn);
|
|
|
|
goto add1_cleanup;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
|
2018-05-26 20:00:23 +03:00
|
|
|
if (!rsa_verify(key)) {
|
|
|
|
pageant_failure_msg(bs, "key is invalid", logctx, logfn);
|
|
|
|
goto add1_cleanup;
|
|
|
|
}
|
|
|
|
|
2015-05-05 22:16:19 +03:00
|
|
|
comment = snewn(commentlen+1, char);
|
|
|
|
if (comment) {
|
|
|
|
memcpy(comment, p + 4, commentlen);
|
|
|
|
comment[commentlen] = '\0';
|
|
|
|
key->comment = comment;
|
|
|
|
}
|
2015-05-06 21:32:26 +03:00
|
|
|
|
|
|
|
if (logfn) {
|
|
|
|
char fingerprint[128];
|
|
|
|
rsa_fingerprint(fingerprint, sizeof(fingerprint), key);
|
|
|
|
plog(logctx, logfn, "submitted key: %s", fingerprint);
|
|
|
|
}
|
|
|
|
|
2015-05-05 22:16:19 +03:00
|
|
|
if (add234(rsakeys, key) == key) {
|
|
|
|
keylist_update();
|
2018-05-24 15:23:17 +03:00
|
|
|
put_byte(bs, SSH_AGENT_SUCCESS);
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
|
2018-05-24 15:23:17 +03:00
|
|
|
key = NULL; /* don't free it in cleanup */
|
2015-05-05 22:16:19 +03:00
|
|
|
} else {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(bs, "key already present",
|
|
|
|
logctx, logfn);
|
|
|
|
}
|
|
|
|
|
|
|
|
add1_cleanup:
|
|
|
|
if (key) {
|
2015-05-05 22:16:19 +03:00
|
|
|
freersakey(key);
|
|
|
|
sfree(key);
|
2018-05-24 15:23:17 +03:00
|
|
|
}
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SSH2_AGENTC_ADD_IDENTITY:
|
|
|
|
/*
|
|
|
|
* Add to the list and return SSH_AGENT_SUCCESS, or
|
|
|
|
* SSH_AGENT_FAILURE if the key was malformed.
|
|
|
|
*/
|
|
|
|
{
|
2018-05-24 15:23:17 +03:00
|
|
|
struct ssh2_userkey *key = NULL;
|
2015-05-05 22:16:19 +03:00
|
|
|
char *comment;
|
|
|
|
const char *alg;
|
|
|
|
int alglen, commlen;
|
|
|
|
int bloblen;
|
|
|
|
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(logctx, logfn, "request: SSH2_AGENTC_ADD_IDENTITY");
|
2015-05-05 22:16:19 +03:00
|
|
|
|
2015-05-06 21:32:26 +03:00
|
|
|
if (msgend < p+4) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before key algorithm",
|
|
|
|
logctx, logfn);
|
|
|
|
goto add2_cleanup;
|
2015-05-06 21:32:26 +03:00
|
|
|
}
|
2015-05-05 22:16:19 +03:00
|
|
|
alglen = toint(GET_32BIT(p));
|
|
|
|
p += 4;
|
2015-05-06 21:32:26 +03:00
|
|
|
if (alglen < 0 || alglen > msgend - p) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before key algorithm",
|
|
|
|
logctx, logfn);
|
|
|
|
goto add2_cleanup;
|
2015-05-06 21:32:26 +03:00
|
|
|
}
|
2015-05-05 22:16:19 +03:00
|
|
|
alg = (const char *)p;
|
|
|
|
p += alglen;
|
|
|
|
|
|
|
|
key = snew(struct ssh2_userkey);
|
2018-05-24 15:23:17 +03:00
|
|
|
key->data = NULL;
|
|
|
|
key->comment = NULL;
|
2015-05-07 21:57:46 +03:00
|
|
|
key->alg = find_pubkey_alg_len(alglen, alg);
|
|
|
|
if (!key->alg) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(bs, "algorithm unknown", logctx, logfn);
|
|
|
|
goto add2_cleanup;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bloblen = msgend - p;
|
2015-05-15 12:12:07 +03:00
|
|
|
key->data = key->alg->openssh_createkey(key->alg, &p, &bloblen);
|
2015-05-05 22:16:19 +03:00
|
|
|
if (!key->data) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(bs, "key setup failed", logctx, logfn);
|
|
|
|
goto add2_cleanup;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* p has been advanced by openssh_createkey, but
|
|
|
|
* certainly not _beyond_ the end of the buffer.
|
|
|
|
*/
|
|
|
|
assert(p <= msgend);
|
|
|
|
|
|
|
|
if (msgend < p+4) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before key comment",
|
|
|
|
logctx, logfn);
|
|
|
|
goto add2_cleanup;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
commlen = toint(GET_32BIT(p));
|
|
|
|
p += 4;
|
|
|
|
|
|
|
|
if (commlen < 0 || commlen > msgend - p) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before key comment",
|
|
|
|
logctx, logfn);
|
|
|
|
goto add2_cleanup;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
comment = snewn(commlen + 1, char);
|
|
|
|
if (comment) {
|
|
|
|
memcpy(comment, p, commlen);
|
|
|
|
comment[commlen] = '\0';
|
|
|
|
}
|
|
|
|
key->comment = comment;
|
|
|
|
|
2015-05-06 21:32:26 +03:00
|
|
|
if (logfn) {
|
2015-05-12 16:35:44 +03:00
|
|
|
char *fingerprint = ssh2_fingerprint(key->alg, key->data);
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(logctx, logfn, "submitted key: %s %s",
|
|
|
|
fingerprint, key->comment);
|
|
|
|
sfree(fingerprint);
|
|
|
|
}
|
|
|
|
|
2015-05-05 22:16:19 +03:00
|
|
|
if (add234(ssh2keys, key) == key) {
|
|
|
|
keylist_update();
|
2018-05-24 15:23:17 +03:00
|
|
|
put_byte(bs, SSH_AGENT_SUCCESS);
|
2015-05-06 21:32:26 +03:00
|
|
|
|
|
|
|
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
|
|
|
|
|
2018-05-24 15:23:17 +03:00
|
|
|
key = NULL; /* don't clean it up */
|
|
|
|
} else {
|
|
|
|
pageant_failure_msg(bs, "key already present",
|
|
|
|
logctx, logfn);
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
2018-05-24 15:23:17 +03:00
|
|
|
|
|
|
|
add2_cleanup:
|
|
|
|
if (key) {
|
|
|
|
if (key->data)
|
|
|
|
key->alg->freekey(key->data);
|
|
|
|
if (key->comment)
|
|
|
|
sfree(key->comment);
|
|
|
|
sfree(key);
|
|
|
|
}
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
|
|
|
|
/*
|
|
|
|
* Remove from the list and return SSH_AGENT_SUCCESS, or
|
|
|
|
* perhaps SSH_AGENT_FAILURE if it wasn't in the list to
|
|
|
|
* start with.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
struct RSAKey reqkey, *key;
|
|
|
|
int n;
|
|
|
|
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(logctx, logfn, "request: SSH1_AGENTC_REMOVE_RSA_IDENTITY");
|
|
|
|
|
2018-05-24 10:22:44 +03:00
|
|
|
n = rsa_ssh1_readpub(p, msgend - p, &reqkey, NULL,
|
|
|
|
RSA_SSH1_EXPONENT_FIRST);
|
2015-05-06 21:32:26 +03:00
|
|
|
if (n < 0) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before public key",
|
|
|
|
logctx, logfn);
|
|
|
|
return;
|
2015-05-06 21:32:26 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (logfn) {
|
|
|
|
char fingerprint[128];
|
|
|
|
reqkey.comment = NULL;
|
|
|
|
rsa_fingerprint(fingerprint, sizeof(fingerprint), &reqkey);
|
|
|
|
plog(logctx, logfn, "unwanted key: %s", fingerprint);
|
|
|
|
}
|
2015-05-05 22:16:19 +03:00
|
|
|
|
|
|
|
key = find234(rsakeys, &reqkey, NULL);
|
|
|
|
freebn(reqkey.exponent);
|
|
|
|
freebn(reqkey.modulus);
|
|
|
|
if (key) {
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(logctx, logfn, "found with comment: %s", key->comment);
|
|
|
|
|
2015-05-05 22:16:19 +03:00
|
|
|
del234(rsakeys, key);
|
|
|
|
keylist_update();
|
|
|
|
freersakey(key);
|
|
|
|
sfree(key);
|
2018-05-24 15:23:17 +03:00
|
|
|
put_byte(bs, SSH_AGENT_SUCCESS);
|
2015-05-06 21:32:26 +03:00
|
|
|
|
|
|
|
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
|
|
|
|
} else {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(bs, "key not found", logctx, logfn);
|
2015-05-06 21:32:26 +03:00
|
|
|
}
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SSH2_AGENTC_REMOVE_IDENTITY:
|
|
|
|
/*
|
|
|
|
* Remove from the list and return SSH_AGENT_SUCCESS, or
|
|
|
|
* perhaps SSH_AGENT_FAILURE if it wasn't in the list to
|
|
|
|
* start with.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
struct ssh2_userkey *key;
|
2018-05-24 12:59:39 +03:00
|
|
|
const void *blobp;
|
|
|
|
int bloblen;
|
2015-05-05 22:16:19 +03:00
|
|
|
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(logctx, logfn, "request: SSH2_AGENTC_REMOVE_IDENTITY");
|
|
|
|
|
|
|
|
if (msgend < p+4) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before public key",
|
|
|
|
logctx, logfn);
|
|
|
|
return;
|
2015-05-06 21:32:26 +03:00
|
|
|
}
|
2018-05-24 12:59:39 +03:00
|
|
|
bloblen = toint(GET_32BIT(p));
|
2015-05-05 22:16:19 +03:00
|
|
|
p += 4;
|
|
|
|
|
2018-05-24 12:59:39 +03:00
|
|
|
if (bloblen < 0 || bloblen > msgend - p) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(
|
|
|
|
bs, "request truncated before public key",
|
|
|
|
logctx, logfn);
|
|
|
|
return;
|
2015-05-06 21:32:26 +03:00
|
|
|
}
|
2018-05-24 12:59:39 +03:00
|
|
|
blobp = p;
|
|
|
|
p += bloblen;
|
2015-05-05 22:16:19 +03:00
|
|
|
|
2015-05-06 21:32:26 +03:00
|
|
|
if (logfn) {
|
2018-05-24 12:59:39 +03:00
|
|
|
char *fingerprint = ssh2_fingerprint_blob(blobp, bloblen);
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(logctx, logfn, "unwanted key: %s", fingerprint);
|
|
|
|
sfree(fingerprint);
|
|
|
|
}
|
|
|
|
|
2018-05-24 12:59:39 +03:00
|
|
|
{
|
|
|
|
strbuf *blob = strbuf_new();
|
|
|
|
put_data(blob, blobp, bloblen);
|
|
|
|
key = find234(ssh2keys, blob, cmpkeys_ssh2_asymm);
|
|
|
|
strbuf_free(blob);
|
|
|
|
}
|
2015-05-06 21:32:26 +03:00
|
|
|
if (!key) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(bs, "key not found", logctx, logfn);
|
|
|
|
return;
|
2015-05-06 21:32:26 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
plog(logctx, logfn, "found with comment: %s", key->comment);
|
2015-05-05 22:16:19 +03:00
|
|
|
|
2015-05-06 21:32:26 +03:00
|
|
|
del234(ssh2keys, key);
|
|
|
|
keylist_update();
|
|
|
|
key->alg->freekey(key->data);
|
|
|
|
sfree(key);
|
2018-05-24 15:23:17 +03:00
|
|
|
put_byte(bs, SSH_AGENT_SUCCESS);
|
2015-05-06 21:32:26 +03:00
|
|
|
|
|
|
|
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
|
|
|
|
/*
|
|
|
|
* Remove all SSH-1 keys. Always returns success.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
struct RSAKey *rkey;
|
|
|
|
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(logctx, logfn, "request:"
|
|
|
|
" SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES");
|
|
|
|
|
2015-05-05 22:16:19 +03:00
|
|
|
while ((rkey = index234(rsakeys, 0)) != NULL) {
|
|
|
|
del234(rsakeys, rkey);
|
|
|
|
freersakey(rkey);
|
|
|
|
sfree(rkey);
|
|
|
|
}
|
|
|
|
keylist_update();
|
|
|
|
|
2018-05-24 15:23:17 +03:00
|
|
|
put_byte(bs, SSH_AGENT_SUCCESS);
|
2015-05-06 21:32:26 +03:00
|
|
|
|
|
|
|
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
|
|
|
|
/*
|
|
|
|
* Remove all SSH-2 keys. Always returns success.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
struct ssh2_userkey *skey;
|
|
|
|
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(logctx, logfn, "request: SSH2_AGENTC_REMOVE_ALL_IDENTITIES");
|
|
|
|
|
2015-05-05 22:16:19 +03:00
|
|
|
while ((skey = index234(ssh2keys, 0)) != NULL) {
|
|
|
|
del234(ssh2keys, skey);
|
|
|
|
skey->alg->freekey(skey->data);
|
|
|
|
sfree(skey);
|
|
|
|
}
|
|
|
|
keylist_update();
|
|
|
|
|
2018-05-24 15:23:17 +03:00
|
|
|
put_byte(bs, SSH_AGENT_SUCCESS);
|
2015-05-06 21:32:26 +03:00
|
|
|
|
|
|
|
plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS");
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(logctx, logfn, "request: unknown message type %d", type);
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(bs, "unrecognised message", logctx, logfn);
|
2015-05-05 22:16:19 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-24 15:23:17 +03:00
|
|
|
void pageant_failure_msg(BinarySink *bs,
|
|
|
|
const char *log_reason,
|
|
|
|
void *logctx, pageant_logfn_t logfn)
|
2015-05-05 22:16:19 +03:00
|
|
|
{
|
2018-05-24 15:23:17 +03:00
|
|
|
put_byte(bs, SSH_AGENT_FAILURE);
|
|
|
|
plog(logctx, logfn, "reply: SSH_AGENT_FAILURE (%s)", log_reason);
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void pageant_init(void)
|
|
|
|
{
|
2015-05-11 17:06:25 +03:00
|
|
|
pageant_local = TRUE;
|
2015-05-05 22:16:19 +03:00
|
|
|
rsakeys = newtree234(cmpkeys_rsa);
|
|
|
|
ssh2keys = newtree234(cmpkeys_ssh2);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct RSAKey *pageant_nth_ssh1_key(int i)
|
|
|
|
{
|
|
|
|
return index234(rsakeys, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ssh2_userkey *pageant_nth_ssh2_key(int i)
|
|
|
|
{
|
|
|
|
return index234(ssh2keys, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
int pageant_count_ssh1_keys(void)
|
|
|
|
{
|
|
|
|
return count234(rsakeys);
|
|
|
|
}
|
|
|
|
|
|
|
|
int pageant_count_ssh2_keys(void)
|
|
|
|
{
|
|
|
|
return count234(ssh2keys);
|
|
|
|
}
|
|
|
|
|
|
|
|
int pageant_add_ssh1_key(struct RSAKey *rkey)
|
|
|
|
{
|
2015-05-07 20:41:06 +03:00
|
|
|
return add234(rsakeys, rkey) == rkey;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int pageant_add_ssh2_key(struct ssh2_userkey *skey)
|
|
|
|
{
|
2015-05-07 20:41:06 +03:00
|
|
|
return add234(ssh2keys, skey) == skey;
|
2015-05-05 22:16:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int pageant_delete_ssh1_key(struct RSAKey *rkey)
|
|
|
|
{
|
|
|
|
struct RSAKey *deleted = del234(rsakeys, rkey);
|
|
|
|
if (!deleted)
|
|
|
|
return FALSE;
|
|
|
|
assert(deleted == rkey);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pageant_delete_ssh2_key(struct ssh2_userkey *skey)
|
|
|
|
{
|
|
|
|
struct ssh2_userkey *deleted = del234(ssh2keys, skey);
|
|
|
|
if (!deleted)
|
|
|
|
return FALSE;
|
|
|
|
assert(deleted == skey);
|
|
|
|
return TRUE;
|
|
|
|
}
|
2015-05-05 22:16:20 +03:00
|
|
|
|
|
|
|
/* ----------------------------------------------------------------------
|
|
|
|
* The agent plug.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Coroutine macros similar to, but simplified from, those in ssh.c.
|
|
|
|
*/
|
|
|
|
#define crBegin(v) { int *crLine = &v; switch(v) { case 0:;
|
2016-06-03 01:03:24 +03:00
|
|
|
#define crFinishV } *crLine = 0; return; }
|
2015-05-05 22:16:20 +03:00
|
|
|
#define crGetChar(c) do \
|
|
|
|
{ \
|
|
|
|
while (len == 0) { \
|
2016-06-03 01:03:24 +03:00
|
|
|
*crLine =__LINE__; return; case __LINE__:; \
|
2015-05-05 22:16:20 +03:00
|
|
|
} \
|
|
|
|
len--; \
|
|
|
|
(c) = (unsigned char)*data++; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
struct pageant_conn_state {
|
|
|
|
Socket connsock;
|
|
|
|
void *logctx;
|
2015-05-06 21:32:26 +03:00
|
|
|
pageant_logfn_t logfn;
|
2015-05-05 22:16:20 +03:00
|
|
|
unsigned char lenbuf[4], pktbuf[AGENT_MAX_MSGLEN];
|
|
|
|
unsigned len, got;
|
|
|
|
int real_packet;
|
|
|
|
int crLine; /* for coroutine in pageant_conn_receive */
|
2018-05-27 11:29:33 +03:00
|
|
|
|
|
|
|
const Plug_vtable *plugvt;
|
2015-05-05 22:16:20 +03:00
|
|
|
};
|
|
|
|
|
2016-06-03 01:03:24 +03:00
|
|
|
static void pageant_conn_closing(Plug plug, const char *error_msg,
|
|
|
|
int error_code, int calling_back)
|
2015-05-05 22:16:20 +03:00
|
|
|
{
|
2018-05-27 11:29:33 +03:00
|
|
|
struct pageant_conn_state *pc = FROMFIELD(
|
|
|
|
plug, struct pageant_conn_state, plugvt);
|
2015-05-06 21:32:26 +03:00
|
|
|
if (error_msg)
|
|
|
|
plog(pc->logctx, pc->logfn, "%p: error: %s", pc, error_msg);
|
|
|
|
else
|
|
|
|
plog(pc->logctx, pc->logfn, "%p: connection closed", pc);
|
2015-05-05 22:16:20 +03:00
|
|
|
sk_close(pc->connsock);
|
|
|
|
sfree(pc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pageant_conn_sent(Plug plug, int bufsize)
|
|
|
|
{
|
2018-05-27 11:29:33 +03:00
|
|
|
/* struct pageant_conn_state *pc = FROMFIELD(
|
|
|
|
plug, struct pageant_conn_state, plugvt); */
|
2015-05-05 22:16:20 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We do nothing here, because we expect that there won't be a
|
|
|
|
* need to throttle and unthrottle the connection to an agent -
|
|
|
|
* clients will typically not send many requests, and will wait
|
|
|
|
* until they receive each reply before sending a new request.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2015-05-06 21:32:26 +03:00
|
|
|
static void pageant_conn_log(void *logctx, const char *fmt, va_list ap)
|
|
|
|
{
|
|
|
|
/* Wrapper on pc->logfn that prefixes the connection identifier */
|
|
|
|
struct pageant_conn_state *pc = (struct pageant_conn_state *)logctx;
|
|
|
|
char *formatted = dupvprintf(fmt, ap);
|
|
|
|
plog(pc->logctx, pc->logfn, "%p: %s", pc, formatted);
|
|
|
|
sfree(formatted);
|
|
|
|
}
|
|
|
|
|
2016-06-03 01:03:24 +03:00
|
|
|
static void pageant_conn_receive(Plug plug, int urgent, char *data, int len)
|
2015-05-05 22:16:20 +03:00
|
|
|
{
|
2018-05-27 11:29:33 +03:00
|
|
|
struct pageant_conn_state *pc = FROMFIELD(
|
|
|
|
plug, struct pageant_conn_state, plugvt);
|
2015-05-05 22:16:20 +03:00
|
|
|
char c;
|
|
|
|
|
|
|
|
crBegin(pc->crLine);
|
|
|
|
|
|
|
|
while (len > 0) {
|
|
|
|
pc->got = 0;
|
|
|
|
while (pc->got < 4) {
|
|
|
|
crGetChar(c);
|
|
|
|
pc->lenbuf[pc->got++] = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
pc->len = GET_32BIT(pc->lenbuf);
|
|
|
|
pc->got = 0;
|
|
|
|
pc->real_packet = (pc->len < AGENT_MAX_MSGLEN-4);
|
|
|
|
|
|
|
|
while (pc->got < pc->len) {
|
|
|
|
crGetChar(c);
|
|
|
|
if (pc->real_packet)
|
|
|
|
pc->pktbuf[pc->got] = c;
|
|
|
|
pc->got++;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2018-05-24 15:23:17 +03:00
|
|
|
strbuf *reply = strbuf_new();
|
|
|
|
|
|
|
|
put_uint32(reply, 0); /* length field to fill in later */
|
2015-05-05 22:16:20 +03:00
|
|
|
|
|
|
|
if (pc->real_packet) {
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_handle_msg(BinarySink_UPCAST(reply), pc->pktbuf, pc->len, pc,
|
|
|
|
pc->logfn ? pageant_conn_log : NULL);
|
2015-05-05 22:16:20 +03:00
|
|
|
} else {
|
2015-05-06 21:32:26 +03:00
|
|
|
plog(pc->logctx, pc->logfn, "%p: overlong message (%u)",
|
|
|
|
pc, pc->len);
|
2018-05-24 15:23:17 +03:00
|
|
|
pageant_failure_msg(BinarySink_UPCAST(reply), "message too long", pc,
|
|
|
|
pc->logfn ? pageant_conn_log : NULL);
|
2015-05-05 22:16:20 +03:00
|
|
|
}
|
2018-05-24 15:23:17 +03:00
|
|
|
|
|
|
|
PUT_32BIT(reply->s, reply->len - 4);
|
|
|
|
sk_write(pc->connsock, reply->s, reply->len);
|
|
|
|
|
|
|
|
strbuf_free(reply);
|
2015-05-05 22:16:20 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-03 01:03:24 +03:00
|
|
|
crFinishV;
|
2015-05-05 22:16:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
struct pageant_listen_state {
|
|
|
|
Socket listensock;
|
|
|
|
void *logctx;
|
2015-05-06 21:32:26 +03:00
|
|
|
pageant_logfn_t logfn;
|
2018-05-27 11:29:33 +03:00
|
|
|
|
|
|
|
const Plug_vtable *plugvt;
|
2015-05-05 22:16:20 +03:00
|
|
|
};
|
|
|
|
|
2016-06-03 01:03:24 +03:00
|
|
|
static void pageant_listen_closing(Plug plug, const char *error_msg,
|
|
|
|
int error_code, int calling_back)
|
2015-05-05 22:16:20 +03:00
|
|
|
{
|
2018-05-27 11:29:33 +03:00
|
|
|
struct pageant_listen_state *pl = FROMFIELD(
|
|
|
|
plug, struct pageant_listen_state, plugvt);
|
2015-05-06 21:32:26 +03:00
|
|
|
if (error_msg)
|
|
|
|
plog(pl->logctx, pl->logfn, "listening socket: error: %s", error_msg);
|
2015-05-05 22:16:20 +03:00
|
|
|
sk_close(pl->listensock);
|
|
|
|
pl->listensock = NULL;
|
|
|
|
}
|
|
|
|
|
2018-05-27 11:29:33 +03:00
|
|
|
static const Plug_vtable pageant_connection_plugvt = {
|
|
|
|
NULL, /* no log function, because that's for outgoing connections */
|
|
|
|
pageant_conn_closing,
|
|
|
|
pageant_conn_receive,
|
|
|
|
pageant_conn_sent,
|
|
|
|
NULL /* no accepting function, because we've already done it */
|
|
|
|
};
|
|
|
|
|
2015-05-05 22:16:20 +03:00
|
|
|
static int pageant_listen_accepting(Plug plug,
|
|
|
|
accept_fn_t constructor, accept_ctx_t ctx)
|
|
|
|
{
|
2018-05-27 11:29:33 +03:00
|
|
|
struct pageant_listen_state *pl = FROMFIELD(
|
|
|
|
plug, struct pageant_listen_state, plugvt);
|
2015-05-05 22:16:20 +03:00
|
|
|
struct pageant_conn_state *pc;
|
|
|
|
const char *err;
|
2015-05-18 15:57:45 +03:00
|
|
|
char *peerinfo;
|
2015-05-05 22:16:20 +03:00
|
|
|
|
|
|
|
pc = snew(struct pageant_conn_state);
|
2018-05-27 11:29:33 +03:00
|
|
|
pc->plugvt = &pageant_connection_plugvt;
|
2015-05-05 22:16:20 +03:00
|
|
|
pc->logfn = pl->logfn;
|
|
|
|
pc->logctx = pl->logctx;
|
|
|
|
pc->crLine = 0;
|
|
|
|
|
2018-05-27 11:29:33 +03:00
|
|
|
pc->connsock = constructor(ctx, &pc->plugvt);
|
2015-05-05 22:16:20 +03:00
|
|
|
if ((err = sk_socket_error(pc->connsock)) != NULL) {
|
|
|
|
sk_close(pc->connsock);
|
|
|
|
sfree(pc);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
sk_set_frozen(pc->connsock, 0);
|
|
|
|
|
2015-05-18 15:57:45 +03:00
|
|
|
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);
|
|
|
|
}
|
2015-05-05 22:16:20 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-05-27 11:29:33 +03:00
|
|
|
static const Plug_vtable pageant_listener_plugvt = {
|
|
|
|
NULL, /* no log function, because that's for outgoing connections */
|
|
|
|
pageant_listen_closing,
|
|
|
|
NULL, /* no receive function on a listening socket */
|
|
|
|
NULL, /* no sent function on a listening socket */
|
|
|
|
pageant_listen_accepting
|
|
|
|
};
|
2015-05-05 22:16:20 +03:00
|
|
|
|
2018-05-27 11:29:33 +03:00
|
|
|
struct pageant_listen_state *pageant_listener_new(Plug *plug)
|
|
|
|
{
|
2015-05-05 22:16:20 +03:00
|
|
|
struct pageant_listen_state *pl = snew(struct pageant_listen_state);
|
2018-05-27 11:29:33 +03:00
|
|
|
pl->plugvt = &pageant_listener_plugvt;
|
2015-05-07 21:04:25 +03:00
|
|
|
pl->logctx = NULL;
|
|
|
|
pl->logfn = NULL;
|
2015-05-05 22:16:20 +03:00
|
|
|
pl->listensock = NULL;
|
2018-05-27 11:29:33 +03:00
|
|
|
*plug = &pl->plugvt;
|
2015-05-05 22:16:20 +03:00
|
|
|
return pl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pageant_listener_got_socket(struct pageant_listen_state *pl, Socket sock)
|
|
|
|
{
|
|
|
|
pl->listensock = sock;
|
|
|
|
}
|
|
|
|
|
2015-05-07 21:04:25 +03:00
|
|
|
void pageant_listener_set_logfn(struct pageant_listen_state *pl,
|
|
|
|
void *logctx, pageant_logfn_t logfn)
|
|
|
|
{
|
|
|
|
pl->logctx = logctx;
|
|
|
|
pl->logfn = logfn;
|
|
|
|
}
|
|
|
|
|
2015-05-05 22:16:20 +03:00
|
|
|
void pageant_listener_free(struct pageant_listen_state *pl)
|
|
|
|
{
|
|
|
|
if (pl->listensock)
|
|
|
|
sk_close(pl->listensock);
|
|
|
|
sfree(pl);
|
|
|
|
}
|
2015-05-11 17:06:25 +03:00
|
|
|
|
|
|
|
/* ----------------------------------------------------------------------
|
|
|
|
* 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)
|
|
|
|
{
|
2015-05-14 11:16:26 +03:00
|
|
|
if (!passphrases) /* in case we never set it up at all */
|
|
|
|
return;
|
|
|
|
|
2015-05-11 17:06:25 +03:00
|
|
|
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) {
|
2018-05-24 15:18:13 +03:00
|
|
|
strbuf *request;
|
|
|
|
unsigned char *response;
|
2015-05-11 17:06:25 +03:00
|
|
|
void *vresponse;
|
2017-01-29 23:24:15 +03:00
|
|
|
int resplen;
|
|
|
|
|
2018-05-24 15:18:13 +03:00
|
|
|
request = strbuf_new_for_agent_query();
|
|
|
|
put_byte(request, SSH1_AGENTC_REQUEST_RSA_IDENTITIES);
|
|
|
|
agent_query_synchronous(request, &vresponse, &resplen);
|
|
|
|
strbuf_free(request);
|
2015-05-11 17:06:25 +03:00
|
|
|
|
|
|
|
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 {
|
2018-05-24 15:23:17 +03:00
|
|
|
strbuf *buf = strbuf_new();
|
|
|
|
pageant_make_keylist1(BinarySink_UPCAST(buf));
|
|
|
|
*length = buf->len;
|
|
|
|
ret = strbuf_to_str(buf);
|
2015-05-11 17:06:25 +03:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *pageant_get_keylist2(int *length)
|
|
|
|
{
|
|
|
|
void *ret;
|
|
|
|
|
|
|
|
if (!pageant_local) {
|
2018-05-24 15:18:13 +03:00
|
|
|
strbuf *request;
|
|
|
|
unsigned char *response;
|
2015-05-11 17:06:25 +03:00
|
|
|
void *vresponse;
|
2017-01-29 23:24:15 +03:00
|
|
|
int resplen;
|
2015-05-11 17:06:25 +03:00
|
|
|
|
2018-05-24 15:18:13 +03:00
|
|
|
request = strbuf_new_for_agent_query();
|
|
|
|
put_byte(request, SSH2_AGENTC_REQUEST_IDENTITIES);
|
|
|
|
agent_query_synchronous(request, &vresponse, &resplen);
|
|
|
|
strbuf_free(request);
|
2015-05-11 17:06:25 +03:00
|
|
|
|
|
|
|
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 {
|
2018-05-24 15:23:17 +03:00
|
|
|
strbuf *buf = strbuf_new();
|
|
|
|
pageant_make_keylist2(BinarySink_UPCAST(buf));
|
|
|
|
*length = buf->len;
|
|
|
|
ret = strbuf_to_str(buf);
|
2015-05-11 17:06:25 +03:00
|
|
|
}
|
|
|
|
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).
|
|
|
|
*/
|
|
|
|
{
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf *blob = strbuf_new();
|
2015-05-11 17:06:25 +03:00
|
|
|
unsigned char *keylist, *p;
|
2018-05-24 12:59:39 +03:00
|
|
|
int i, nkeys, keylistlen;
|
2015-05-11 17:06:25 +03:00
|
|
|
|
|
|
|
if (type == SSH_KEYTYPE_SSH1) {
|
2018-05-24 12:59:39 +03:00
|
|
|
if (!rsa_ssh1_loadpub(filename, BinarySink_UPCAST(blob), NULL, &error)) {
|
2015-05-11 17:06:25 +03:00
|
|
|
*retstr = dupprintf("Couldn't load private key (%s)", error);
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf_free(blob);
|
2015-05-11 17:06:25 +03:00
|
|
|
return PAGEANT_ACTION_FAILURE;
|
|
|
|
}
|
|
|
|
keylist = pageant_get_keylist1(&keylistlen);
|
|
|
|
} else {
|
2018-05-24 12:59:39 +03:00
|
|
|
/* For our purposes we want the blob prefixed with its
|
|
|
|
* length, so add a placeholder here to fill in
|
|
|
|
* afterwards */
|
|
|
|
put_uint32(blob, 0);
|
|
|
|
if (!ssh2_userkey_loadpub(filename, NULL, BinarySink_UPCAST(blob),
|
|
|
|
NULL, &error)) {
|
2015-05-11 17:06:25 +03:00
|
|
|
*retstr = dupprintf("Couldn't load private key (%s)", error);
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf_free(blob);
|
2015-05-11 17:06:25 +03:00
|
|
|
return PAGEANT_ACTION_FAILURE;
|
|
|
|
}
|
2018-05-24 12:59:39 +03:00
|
|
|
PUT_32BIT(blob->s, blob->len - 4);
|
2015-05-11 17:06:25 +03:00
|
|
|
keylist = pageant_get_keylist2(&keylistlen);
|
|
|
|
}
|
|
|
|
if (keylist) {
|
|
|
|
if (keylistlen < 4) {
|
|
|
|
*retstr = dupstr("Received broken key list from agent");
|
2017-02-15 08:31:51 +03:00
|
|
|
sfree(keylist);
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf_free(blob);
|
2015-05-11 17:06:25 +03:00
|
|
|
return PAGEANT_ACTION_FAILURE;
|
|
|
|
}
|
|
|
|
nkeys = toint(GET_32BIT(keylist));
|
|
|
|
if (nkeys < 0) {
|
|
|
|
*retstr = dupstr("Received broken key list from agent");
|
2017-02-15 08:31:51 +03:00
|
|
|
sfree(keylist);
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf_free(blob);
|
2015-05-11 17:06:25 +03:00
|
|
|
return PAGEANT_ACTION_FAILURE;
|
|
|
|
}
|
|
|
|
p = keylist + 4;
|
|
|
|
keylistlen -= 4;
|
|
|
|
|
|
|
|
for (i = 0; i < nkeys; i++) {
|
2018-05-24 12:59:39 +03:00
|
|
|
if (!memcmp(blob->s, p, blob->len)) {
|
2015-05-11 17:06:25 +03:00
|
|
|
/* Key is already present; we can now leave. */
|
|
|
|
sfree(keylist);
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf_free(blob);
|
2015-05-11 17:06:25 +03:00
|
|
|
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");
|
2017-02-14 23:42:26 +03:00
|
|
|
sfree(keylist);
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf_free(blob);
|
2015-05-11 17:06:25 +03:00
|
|
|
return PAGEANT_ACTION_FAILURE;
|
|
|
|
}
|
|
|
|
p += n;
|
|
|
|
keylistlen -= n;
|
|
|
|
} else {
|
|
|
|
int n;
|
|
|
|
if (keylistlen < 4) {
|
|
|
|
*retstr = dupstr("Received broken key list from agent");
|
2017-02-14 23:42:26 +03:00
|
|
|
sfree(keylist);
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf_free(blob);
|
2015-05-11 17:06:25 +03:00
|
|
|
return PAGEANT_ACTION_FAILURE;
|
|
|
|
}
|
Tweak bounds checks in pageant_add_keyfile.
When we're going through the response from an SSH agent we asked for a
list of keys, and processing the string lengths in the SSH-2 sequence
of (public blob, comment) pairs, we were adding 4 to each string
length, and although we checked if the result came out to a negative
value (if interpreted as a signed integer) or a positive one going
beyond the end of the response buffer, we didn't check if it wrapped
round to a positive value less than 4. As a result, if an agent
returned malformed data sent a length field of 0xFFFFFFFC, the pointer
would advance no distance at all through the buffer, and the next
iteration of the loop would check the same length field again.
(However, this would only consume CPU pointlessly for a limited time,
because the outer loop up to 'nkeys' would still terminate sooner or
later. Also, I don't think this can sensibly be classed as a serious
security hazard - it's arguably a borderline DoS, but it requires a
hostile SSH _agent_ if data of that type is to be sent on purpose, and
an untrusted SSH agent is not part of the normal security model!)
2017-01-23 23:08:18 +03:00
|
|
|
n = GET_32BIT(p);
|
|
|
|
p += 4;
|
|
|
|
keylistlen -= 4;
|
|
|
|
|
|
|
|
if (n < 0 || n > keylistlen) {
|
2015-05-11 17:06:25 +03:00
|
|
|
*retstr = dupstr("Received broken key list from agent");
|
2017-02-14 23:42:26 +03:00
|
|
|
sfree(keylist);
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf_free(blob);
|
2015-05-11 17:06:25 +03:00
|
|
|
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");
|
2017-02-14 23:42:26 +03:00
|
|
|
sfree(keylist);
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf_free(blob);
|
2015-05-11 17:06:25 +03:00
|
|
|
return PAGEANT_ACTION_FAILURE;
|
|
|
|
}
|
Tweak bounds checks in pageant_add_keyfile.
When we're going through the response from an SSH agent we asked for a
list of keys, and processing the string lengths in the SSH-2 sequence
of (public blob, comment) pairs, we were adding 4 to each string
length, and although we checked if the result came out to a negative
value (if interpreted as a signed integer) or a positive one going
beyond the end of the response buffer, we didn't check if it wrapped
round to a positive value less than 4. As a result, if an agent
returned malformed data sent a length field of 0xFFFFFFFC, the pointer
would advance no distance at all through the buffer, and the next
iteration of the loop would check the same length field again.
(However, this would only consume CPU pointlessly for a limited time,
because the outer loop up to 'nkeys' would still terminate sooner or
later. Also, I don't think this can sensibly be classed as a serious
security hazard - it's arguably a borderline DoS, but it requires a
hostile SSH _agent_ if data of that type is to be sent on purpose, and
an untrusted SSH agent is not part of the normal security model!)
2017-01-23 23:08:18 +03:00
|
|
|
n = GET_32BIT(p);
|
|
|
|
p += 4;
|
|
|
|
keylistlen -= 4;
|
|
|
|
|
|
|
|
if (n < 0 || n > keylistlen) {
|
2015-05-11 17:06:25 +03:00
|
|
|
*retstr = dupstr("Received broken key list from agent");
|
2017-02-14 23:42:26 +03:00
|
|
|
sfree(keylist);
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf_free(blob);
|
2015-05-11 17:06:25 +03:00
|
|
|
return PAGEANT_ACTION_FAILURE;
|
|
|
|
}
|
|
|
|
p += n;
|
|
|
|
keylistlen -= n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sfree(keylist);
|
|
|
|
}
|
|
|
|
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf_free(blob);
|
2015-05-11 17:06:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
error = NULL;
|
|
|
|
if (type == SSH_KEYTYPE_SSH1)
|
2018-05-24 10:22:44 +03:00
|
|
|
needs_pass = rsa_ssh1_encrypted(filename, &comment);
|
2015-05-11 17:06:25 +03:00
|
|
|
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;
|
2017-02-14 23:42:26 +03:00
|
|
|
sfree(rkey);
|
2015-05-11 17:06:25 +03:00
|
|
|
return PAGEANT_ACTION_NEED_PP;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
this_passphrase = "";
|
|
|
|
|
|
|
|
if (type == SSH_KEYTYPE_SSH1)
|
2018-05-24 10:22:44 +03:00
|
|
|
ret = rsa_ssh1_loadkey(filename, rkey, this_passphrase, &error);
|
2015-05-11 17:06:25 +03:00
|
|
|
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);
|
2017-02-14 23:42:26 +03:00
|
|
|
sfree(rkey);
|
2015-05-11 17:06:25 +03:00
|
|
|
return PAGEANT_ACTION_FAILURE;
|
|
|
|
} else if (ret == 1) {
|
|
|
|
/*
|
|
|
|
* Successfully loaded the key file.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Passphrase wasn't right; go round again.
|
|
|
|
*/
|
|
|
|
attempts++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-04-15 11:06:22 +03:00
|
|
|
* If we get here, we've successfully loaded the key into
|
2015-05-11 17:06:25 +03:00
|
|
|
* 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) {
|
2018-05-24 15:18:13 +03:00
|
|
|
strbuf *request;
|
|
|
|
unsigned char *response;
|
2015-05-11 17:06:25 +03:00
|
|
|
void *vresponse;
|
2018-05-24 15:18:13 +03:00
|
|
|
int resplen;
|
|
|
|
|
|
|
|
request = strbuf_new_for_agent_query();
|
|
|
|
put_byte(request, SSH1_AGENTC_ADD_RSA_IDENTITY);
|
|
|
|
put_uint32(request, bignum_bitcount(rkey->modulus));
|
|
|
|
put_mp_ssh1(request, rkey->modulus);
|
|
|
|
put_mp_ssh1(request, rkey->exponent);
|
|
|
|
put_mp_ssh1(request, rkey->private_exponent);
|
|
|
|
put_mp_ssh1(request, rkey->iqmp);
|
|
|
|
put_mp_ssh1(request, rkey->q);
|
|
|
|
put_mp_ssh1(request, rkey->p);
|
|
|
|
put_stringz(request, rkey->comment);
|
|
|
|
agent_query_synchronous(request, &vresponse, &resplen);
|
|
|
|
strbuf_free(request);
|
|
|
|
|
2015-05-11 17:06:25 +03:00
|
|
|
response = vresponse;
|
|
|
|
if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) {
|
|
|
|
*retstr = dupstr("The already running Pageant "
|
|
|
|
"refused to add the key.");
|
2017-02-14 23:42:26 +03:00
|
|
|
freersakey(rkey);
|
|
|
|
sfree(rkey);
|
|
|
|
sfree(response);
|
2015-05-11 17:06:25 +03:00
|
|
|
return PAGEANT_ACTION_FAILURE;
|
|
|
|
}
|
2017-02-14 23:42:26 +03:00
|
|
|
freersakey(rkey);
|
|
|
|
sfree(rkey);
|
2015-05-11 17:06:25 +03:00
|
|
|
sfree(response);
|
|
|
|
} else {
|
|
|
|
if (!pageant_add_ssh1_key(rkey)) {
|
2017-02-14 23:42:26 +03:00
|
|
|
freersakey(rkey);
|
2015-05-11 17:06:25 +03:00
|
|
|
sfree(rkey); /* already present, don't waste RAM */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!pageant_local) {
|
2018-05-24 15:18:13 +03:00
|
|
|
strbuf *request;
|
|
|
|
unsigned char *response;
|
2015-05-11 17:06:25 +03:00
|
|
|
void *vresponse;
|
2018-05-24 15:18:13 +03:00
|
|
|
int resplen;
|
|
|
|
|
|
|
|
request = strbuf_new_for_agent_query();
|
|
|
|
put_byte(request, SSH2_AGENTC_ADD_IDENTITY);
|
|
|
|
put_stringz(request, skey->alg->name);
|
|
|
|
skey->alg->openssh_fmtkey(skey->data, BinarySink_UPCAST(request));
|
|
|
|
put_stringz(request, skey->comment);
|
|
|
|
agent_query_synchronous(request, &vresponse, &resplen);
|
|
|
|
strbuf_free(request);
|
|
|
|
|
2015-05-11 17:06:25 +03:00
|
|
|
response = vresponse;
|
|
|
|
if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) {
|
|
|
|
*retstr = dupstr("The already running Pageant "
|
|
|
|
"refused to add the key.");
|
2017-02-14 23:42:26 +03:00
|
|
|
sfree(response);
|
2015-05-11 17:06:25 +03:00
|
|
|
return PAGEANT_ACTION_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2015-05-11 20:34:45 +03:00
|
|
|
|
|
|
|
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;
|
2015-05-12 15:27:33 +03:00
|
|
|
struct pageant_pubkey cbkey;
|
2015-05-11 20:34:45 +03:00
|
|
|
|
|
|
|
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));
|
2018-05-24 10:22:44 +03:00
|
|
|
n = rsa_ssh1_readpub(p, keylistlen, &rkey, NULL,
|
|
|
|
RSA_SSH1_EXPONENT_FIRST);
|
2015-05-11 20:34:45 +03:00
|
|
|
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;
|
|
|
|
|
2018-05-24 12:59:39 +03:00
|
|
|
cbkey.blob = strbuf_new();
|
|
|
|
rsa_ssh1_public_blob(BinarySink_UPCAST(cbkey.blob), &rkey,
|
|
|
|
RSA_SSH1_EXPONENT_FIRST);
|
2015-05-12 16:48:32 +03:00
|
|
|
cbkey.comment = comment;
|
2015-05-12 15:27:33 +03:00
|
|
|
cbkey.ssh_version = 1;
|
|
|
|
callback(callback_ctx, fingerprint, comment, &cbkey);
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf_free(cbkey.blob);
|
2015-05-11 20:34:45 +03:00
|
|
|
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;
|
|
|
|
}
|
2015-05-12 16:35:44 +03:00
|
|
|
fingerprint = ssh2_fingerprint_blob(p, n);
|
2018-05-24 12:59:39 +03:00
|
|
|
cbkey.blob = strbuf_new();
|
|
|
|
put_data(cbkey.blob, p, n);
|
2015-05-11 20:34:45 +03:00
|
|
|
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;
|
|
|
|
|
2015-05-12 15:27:33 +03:00
|
|
|
cbkey.ssh_version = 2;
|
2015-05-12 16:48:32 +03:00
|
|
|
cbkey.comment = comment;
|
2015-05-12 15:27:33 +03:00
|
|
|
callback(callback_ctx, fingerprint, comment, &cbkey);
|
2015-05-11 20:34:45 +03:00
|
|
|
sfree(fingerprint);
|
|
|
|
sfree(comment);
|
|
|
|
}
|
|
|
|
|
|
|
|
sfree(keylist);
|
|
|
|
|
|
|
|
if (keylistlen != 0) {
|
2018-05-25 16:01:59 +03:00
|
|
|
*retstr = dupstr("Received broken SSH-2 key list from agent");
|
2015-05-11 20:34:45 +03:00
|
|
|
return PAGEANT_ACTION_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PAGEANT_ACTION_OK;
|
|
|
|
}
|
2015-05-12 15:27:33 +03:00
|
|
|
|
|
|
|
int pageant_delete_key(struct pageant_pubkey *key, char **retstr)
|
|
|
|
{
|
2018-05-24 15:18:13 +03:00
|
|
|
strbuf *request;
|
|
|
|
unsigned char *response;
|
|
|
|
int resplen, ret;
|
2015-05-12 15:27:33 +03:00
|
|
|
void *vresponse;
|
|
|
|
|
2018-05-24 15:18:13 +03:00
|
|
|
request = strbuf_new_for_agent_query();
|
|
|
|
|
2015-05-12 15:27:33 +03:00
|
|
|
if (key->ssh_version == 1) {
|
2018-05-24 15:18:13 +03:00
|
|
|
put_byte(request, SSH1_AGENTC_REMOVE_RSA_IDENTITY);
|
|
|
|
put_data(request, key->blob->s, key->blob->len);
|
2015-05-12 15:27:33 +03:00
|
|
|
} else {
|
2018-05-24 15:18:13 +03:00
|
|
|
put_byte(request, SSH2_AGENTC_REMOVE_IDENTITY);
|
|
|
|
put_string(request, key->blob->s, key->blob->len);
|
2015-05-12 15:27:33 +03:00
|
|
|
}
|
|
|
|
|
2018-05-24 15:18:13 +03:00
|
|
|
agent_query_synchronous(request, &vresponse, &resplen);
|
|
|
|
strbuf_free(request);
|
|
|
|
|
2015-05-12 15:27:33 +03:00
|
|
|
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(response);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-05-12 16:55:44 +03:00
|
|
|
int pageant_delete_all_keys(char **retstr)
|
|
|
|
{
|
2018-05-24 15:18:13 +03:00
|
|
|
strbuf *request;
|
|
|
|
unsigned char *response;
|
|
|
|
int resplen, success;
|
2015-05-12 16:55:44 +03:00
|
|
|
void *vresponse;
|
|
|
|
|
2018-05-24 15:18:13 +03:00
|
|
|
request = strbuf_new_for_agent_query();
|
|
|
|
put_byte(request, SSH2_AGENTC_REMOVE_ALL_IDENTITIES);
|
|
|
|
agent_query_synchronous(request, &vresponse, &resplen);
|
|
|
|
strbuf_free(request);
|
2015-05-12 16:55:44 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-05-24 15:18:13 +03:00
|
|
|
request = strbuf_new_for_agent_query();
|
|
|
|
put_byte(request, SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES);
|
|
|
|
agent_query_synchronous(request, &vresponse, &resplen);
|
|
|
|
strbuf_free(request);
|
2015-05-12 16:55:44 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-05-12 15:27:33 +03:00
|
|
|
struct pageant_pubkey *pageant_pubkey_copy(struct pageant_pubkey *key)
|
|
|
|
{
|
|
|
|
struct pageant_pubkey *ret = snew(struct pageant_pubkey);
|
2018-05-24 12:59:39 +03:00
|
|
|
ret->blob = strbuf_new();
|
|
|
|
put_data(ret->blob, key->blob->s, key->blob->len);
|
2015-05-12 16:48:32 +03:00
|
|
|
ret->comment = key->comment ? dupstr(key->comment) : NULL;
|
2015-05-12 15:27:33 +03:00
|
|
|
ret->ssh_version = key->ssh_version;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pageant_pubkey_free(struct pageant_pubkey *key)
|
|
|
|
{
|
2015-05-12 16:48:32 +03:00
|
|
|
sfree(key->comment);
|
2018-05-24 12:59:39 +03:00
|
|
|
strbuf_free(key->blob);
|
2015-05-12 15:27:33 +03:00
|
|
|
sfree(key);
|
|
|
|
}
|