]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
Move half of Pageant out into a cross-platform source file.
authorSimon Tatham <anakin@pobox.com>
Tue, 5 May 2015 19:16:19 +0000 (20:16 +0100)
committerSimon Tatham <anakin@pobox.com>
Tue, 5 May 2015 19:16:19 +0000 (20:16 +0100)
I'm aiming for windows/winpgnt.c to only contain the parts of Windows
Pageant that are actually to do with handling the Windows API, and for
all the actual agent logic to be cross-platform.

This commit is a start: I've moved every function and internal
variable that was easy to move. But it doesn't get all the way there -
there's still a lot of logic in add_keyfile() and get_keylist*() that
would be good to move out to cross-platform code, but it's harder
because that code is currently quite intertwined with details of
Windows OS interfacing such as printing message boxes and passphrase
prompts and calling back out to agent_query if the Pageant doing that
job isn't the primary one.

Recipe
pageant.c [new file with mode: 0644]
pageant.h [new file with mode: 0644]
windows/winpgnt.c

diff --git a/Recipe b/Recipe
index 584855271b6d9e38cc9379d1ac2f29e74ce2159e..fa8a1a44316d2dce2aeff6d0ce7e0bc7a7b86f7b 100644 (file)
--- a/Recipe
+++ b/Recipe
@@ -272,9 +272,9 @@ pscp     : [C] pscp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC
 psftp    : [C] psftp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC
          + psftp.res winnojmp LIBS
 
-pageant  : [G] winpgnt sshrsa sshpubk sshdes sshbn sshmd5 version tree234
-         + misc sshaes sshsha winsecur winpgntc sshdss sshsh256 sshsh512
-         + winutils sshecc winmisc winhelp conf pageant.res LIBS
+pageant  : [G] winpgnt pageant sshrsa sshpubk sshdes sshbn sshmd5 version
+        + tree234 misc sshaes sshsha winsecur winpgntc sshdss sshsh256
+        + sshsh512 winutils sshecc winmisc winhelp conf pageant.res LIBS
 
 puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
          + sshrand winnoise sshsha winstore misc winctrls sshrsa sshdss winmisc
diff --git a/pageant.c b/pageant.c
new file mode 100644 (file)
index 0000000..47e09db
--- /dev/null
+++ b/pageant.c
@@ -0,0 +1,767 @@
+/*
+ * 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"
+
+/*
+ * We need this to link with the RSA code, because rsaencrypt()
+ * pads its data with random bytes. Since we only use rsadecrypt()
+ * 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 */
+}
+
+/*
+ * rsakeys stores SSH-1 RSA keys. ssh2keys stores all SSH-2 keys.
+ */
+static tree234 *rsakeys, *ssh2keys;
+
+/*
+ * Blob structure for passing to the asymmetric SSH-2 key compare
+ * function, prototyped here.
+ */
+struct blob {
+    const unsigned char *blob;
+    int len;
+};
+static int cmpkeys_ssh2_asymm(void *av, void *bv);
+
+/*
+ * 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;
+}
+
+/*
+ * Key comparison function for the 2-3-4 tree of SSH-2 keys.
+ */
+static int cmpkeys_ssh2(void *av, void *bv)
+{
+    struct ssh2_userkey *a = (struct ssh2_userkey *) av;
+    struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
+    int i;
+    int alen, blen;
+    unsigned char *ablob, *bblob;
+    int c;
+
+    /*
+     * Compare purely by public blob.
+     */
+    ablob = a->alg->public_blob(a->data, &alen);
+    bblob = b->alg->public_blob(b->data, &blen);
+
+    c = 0;
+    for (i = 0; i < alen && i < blen; i++) {
+       if (ablob[i] < bblob[i]) {
+           c = -1;
+           break;
+       } else if (ablob[i] > bblob[i]) {
+           c = +1;
+           break;
+       }
+    }
+    if (c == 0 && i < alen)
+       c = +1;                        /* a is longer */
+    if (c == 0 && i < blen)
+       c = -1;                        /* a is longer */
+
+    sfree(ablob);
+    sfree(bblob);
+
+    return c;
+}
+
+/*
+ * Key comparison function for looking up a blob in the 2-3-4 tree
+ * of SSH-2 keys.
+ */
+static int cmpkeys_ssh2_asymm(void *av, void *bv)
+{
+    struct blob *a = (struct blob *) av;
+    struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
+    int i;
+    int alen, blen;
+    const unsigned char *ablob;
+    unsigned char *bblob;
+    int c;
+
+    /*
+     * Compare purely by public blob.
+     */
+    ablob = a->blob;
+    alen = a->len;
+    bblob = b->alg->public_blob(b->data, &blen);
+
+    c = 0;
+    for (i = 0; i < alen && i < blen; i++) {
+       if (ablob[i] < bblob[i]) {
+           c = -1;
+           break;
+       } else if (ablob[i] > bblob[i]) {
+           c = +1;
+           break;
+       }
+    }
+    if (c == 0 && i < alen)
+       c = +1;                        /* a is longer */
+    if (c == 0 && i < blen)
+       c = -1;                        /* a is longer */
+
+    sfree(bblob);
+
+    return c;
+}
+
+/*
+ * Create an SSH-1 key list in a malloc'ed buffer; return its
+ * length.
+ */
+void *pageant_make_keylist1(int *length)
+{
+    int i, nkeys, len;
+    struct RSAKey *key;
+    unsigned char *blob, *p, *ret;
+    int bloblen;
+
+    /*
+     * Count up the number and length of keys we hold.
+     */
+    len = 4;
+    nkeys = 0;
+    for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
+       nkeys++;
+       blob = rsa_public_blob(key, &bloblen);
+       len += bloblen;
+       sfree(blob);
+       len += 4 + strlen(key->comment);
+    }
+
+    /* Allocate the buffer. */
+    p = ret = snewn(len, unsigned char);
+    if (length) *length = len;
+
+    PUT_32BIT(p, nkeys);
+    p += 4;
+    for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
+       blob = rsa_public_blob(key, &bloblen);
+       memcpy(p, blob, bloblen);
+       p += bloblen;
+       sfree(blob);
+       PUT_32BIT(p, strlen(key->comment));
+       memcpy(p + 4, key->comment, strlen(key->comment));
+       p += 4 + strlen(key->comment);
+    }
+
+    assert(p - ret == len);
+    return ret;
+}
+
+/*
+ * Create an SSH-2 key list in a malloc'ed buffer; return its
+ * length.
+ */
+void *pageant_make_keylist2(int *length)
+{
+    struct ssh2_userkey *key;
+    int i, len, nkeys;
+    unsigned char *blob, *p, *ret;
+    int bloblen;
+
+    /*
+     * Count up the number and length of keys we hold.
+     */
+    len = 4;
+    nkeys = 0;
+    for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
+       nkeys++;
+       len += 4;              /* length field */
+       blob = key->alg->public_blob(key->data, &bloblen);
+       len += bloblen;
+       sfree(blob);
+       len += 4 + strlen(key->comment);
+    }
+
+    /* Allocate the buffer. */
+    p = ret = snewn(len, unsigned char);
+    if (length) *length = len;
+
+    /*
+     * Packet header is the obvious five bytes, plus four
+     * bytes for the key count.
+     */
+    PUT_32BIT(p, nkeys);
+    p += 4;
+    for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
+       blob = key->alg->public_blob(key->data, &bloblen);
+       PUT_32BIT(p, bloblen);
+       p += 4;
+       memcpy(p, blob, bloblen);
+       p += bloblen;
+       sfree(blob);
+       PUT_32BIT(p, strlen(key->comment));
+       memcpy(p + 4, key->comment, strlen(key->comment));
+       p += 4 + strlen(key->comment);
+    }
+
+    assert(p - ret == len);
+    return ret;
+}
+
+void *pageant_handle_msg(const void *msg, int msglen, int *outlen)
+{
+    const unsigned char *p = msg;
+    const unsigned char *msgend;
+    unsigned char *ret = snewn(AGENT_MAX_MSGLEN, unsigned char);
+    int type;
+
+    msgend = p + msglen;
+
+    /*
+     * Get the message type.
+     */
+    if (msgend < p+1)
+       goto failure;
+    type = *p++;
+
+    switch (type) {
+      case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
+       /*
+        * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
+        */
+       {
+           int len;
+           void *keylist;
+
+           ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
+           keylist = pageant_make_keylist1(&len);
+           if (len + 5 > AGENT_MAX_MSGLEN) {
+               sfree(keylist);
+               goto failure;
+           }
+           PUT_32BIT(ret, len + 1);
+           memcpy(ret + 5, keylist, len);
+           sfree(keylist);
+       }
+       break;
+      case SSH2_AGENTC_REQUEST_IDENTITIES:
+       /*
+        * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
+        */
+       {
+           int len;
+           void *keylist;
+
+           ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
+           keylist = pageant_make_keylist2(&len);
+           if (len + 5 > AGENT_MAX_MSGLEN) {
+               sfree(keylist);
+               goto failure;
+           }
+           PUT_32BIT(ret, len + 1);
+           memcpy(ret + 5, keylist, len);
+           sfree(keylist);
+       }
+       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;
+           int i, len;
+
+           p += 4;
+           i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
+           if (i < 0)
+               goto failure;
+           p += i;
+           i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
+           if (i < 0) {
+                freebn(reqkey.exponent);
+               goto failure;
+            }
+           p += i;
+           i = ssh1_read_bignum(p, msgend - p, &challenge);
+           if (i < 0) {
+                freebn(reqkey.exponent);
+                freebn(reqkey.modulus);
+               goto failure;
+            }
+           p += i;
+           if (msgend < p+16) {
+               freebn(reqkey.exponent);
+               freebn(reqkey.modulus);
+               freebn(challenge);
+               goto failure;
+           }
+           memcpy(response_source + 32, p, 16);
+           p += 16;
+           if (msgend < p+4 ||
+               GET_32BIT(p) != 1 ||
+               (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
+               freebn(reqkey.exponent);
+               freebn(reqkey.modulus);
+               freebn(challenge);
+               goto failure;
+           }
+           response = rsadecrypt(challenge, key);
+           for (i = 0; i < 32; i++)
+               response_source[i] = bignum_byte(response, 31 - i);
+
+           MD5Init(&md5c);
+           MD5Update(&md5c, response_source, 48);
+           MD5Final(response_md5, &md5c);
+           smemclr(response_source, 48);       /* burn the evidence */
+           freebn(response);          /* and that evidence */
+           freebn(challenge);         /* yes, and that evidence */
+           freebn(reqkey.exponent);   /* and free some memory ... */
+           freebn(reqkey.modulus);    /* ... while we're at it. */
+
+           /*
+            * Packet is the obvious five byte header, plus sixteen
+            * bytes of MD5.
+            */
+           len = 5 + 16;
+           PUT_32BIT(ret, len - 4);
+           ret[4] = SSH1_AGENT_RSA_RESPONSE;
+           memcpy(ret + 5, response_md5, 16);
+       }
+       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;
+           struct blob b;
+           const unsigned char *data;
+            unsigned char *signature;
+           int datalen, siglen, len;
+
+           if (msgend < p+4)
+               goto failure;
+           b.len = toint(GET_32BIT(p));
+            if (b.len < 0 || b.len > msgend - (p+4))
+                goto failure;
+           p += 4;
+           b.blob = p;
+           p += b.len;
+           if (msgend < p+4)
+               goto failure;
+           datalen = toint(GET_32BIT(p));
+           p += 4;
+           if (datalen < 0 || datalen > msgend - p)
+               goto failure;
+           data = p;
+           key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
+           if (!key)
+               goto failure;
+           signature = key->alg->sign(key->data, (const char *)data,
+                                       datalen, &siglen);
+           len = 5 + 4 + siglen;
+           PUT_32BIT(ret, len - 4);
+           ret[4] = SSH2_AGENT_SIGN_RESPONSE;
+           PUT_32BIT(ret + 5, siglen);
+           memcpy(ret + 5 + 4, signature, siglen);
+           sfree(signature);
+       }
+       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;
+
+           key = snew(struct RSAKey);
+           memset(key, 0, sizeof(struct RSAKey));
+
+           n = makekey(p, msgend - p, key, NULL, 1);
+           if (n < 0) {
+               freersakey(key);
+               sfree(key);
+               goto failure;
+           }
+           p += n;
+
+           n = makeprivate(p, msgend - p, key);
+           if (n < 0) {
+               freersakey(key);
+               sfree(key);
+               goto failure;
+           }
+           p += n;
+
+           n = ssh1_read_bignum(p, msgend - p, &key->iqmp);  /* p^-1 mod q */
+           if (n < 0) {
+               freersakey(key);
+               sfree(key);
+               goto failure;
+           }
+           p += n;
+
+           n = ssh1_read_bignum(p, msgend - p, &key->p);  /* p */
+           if (n < 0) {
+               freersakey(key);
+               sfree(key);
+               goto failure;
+           }
+           p += n;
+
+           n = ssh1_read_bignum(p, msgend - p, &key->q);  /* q */
+           if (n < 0) {
+               freersakey(key);
+               sfree(key);
+               goto failure;
+           }
+           p += n;
+
+           if (msgend < p+4) {
+               freersakey(key);
+               sfree(key);
+               goto failure;
+           }
+            commentlen = toint(GET_32BIT(p));
+
+           if (commentlen < 0 || commentlen > msgend - p) {
+               freersakey(key);
+               sfree(key);
+               goto failure;
+           }
+
+           comment = snewn(commentlen+1, char);
+           if (comment) {
+               memcpy(comment, p + 4, commentlen);
+                comment[commentlen] = '\0';
+               key->comment = comment;
+           }
+           PUT_32BIT(ret, 1);
+           ret[4] = SSH_AGENT_FAILURE;
+           if (add234(rsakeys, key) == key) {
+               keylist_update();
+               ret[4] = SSH_AGENT_SUCCESS;
+           } else {
+               freersakey(key);
+               sfree(key);
+           }
+       }
+       break;
+      case SSH2_AGENTC_ADD_IDENTITY:
+       /*
+        * Add to the list and return SSH_AGENT_SUCCESS, or
+        * SSH_AGENT_FAILURE if the key was malformed.
+        */
+       {
+           struct ssh2_userkey *key;
+           char *comment;
+            const char *alg;
+           int alglen, commlen;
+           int bloblen;
+
+
+           if (msgend < p+4)
+               goto failure;
+           alglen = toint(GET_32BIT(p));
+           p += 4;
+           if (alglen < 0 || alglen > msgend - p)
+               goto failure;
+           alg = (const char *)p;
+           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 {
+               sfree(key);
+               goto failure;
+           }
+
+           bloblen = msgend - p;
+           key->data = key->alg->openssh_createkey(&p, &bloblen);
+           if (!key->data) {
+               sfree(key);
+               goto failure;
+           }
+
+           /*
+            * p has been advanced by openssh_createkey, but
+            * certainly not _beyond_ the end of the buffer.
+            */
+           assert(p <= msgend);
+
+           if (msgend < p+4) {
+               key->alg->freekey(key->data);
+               sfree(key);
+               goto failure;
+           }
+           commlen = toint(GET_32BIT(p));
+           p += 4;
+
+           if (commlen < 0 || commlen > msgend - p) {
+               key->alg->freekey(key->data);
+               sfree(key);
+               goto failure;
+           }
+           comment = snewn(commlen + 1, char);
+           if (comment) {
+               memcpy(comment, p, commlen);
+               comment[commlen] = '\0';
+           }
+           key->comment = comment;
+
+           PUT_32BIT(ret, 1);
+           ret[4] = SSH_AGENT_FAILURE;
+           if (add234(ssh2keys, key) == key) {
+               keylist_update();
+               ret[4] = SSH_AGENT_SUCCESS;
+           } else {
+               key->alg->freekey(key->data);
+               sfree(key->comment);
+               sfree(key);
+           }
+       }
+       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;
+
+           n = makekey(p, msgend - p, &reqkey, NULL, 0);
+           if (n < 0)
+               goto failure;
+
+           key = find234(rsakeys, &reqkey, NULL);
+           freebn(reqkey.exponent);
+           freebn(reqkey.modulus);
+           PUT_32BIT(ret, 1);
+           ret[4] = SSH_AGENT_FAILURE;
+           if (key) {
+               del234(rsakeys, key);
+               keylist_update();
+               freersakey(key);
+               sfree(key);
+               ret[4] = SSH_AGENT_SUCCESS;
+           }
+       }
+       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;
+           struct blob b;
+
+           if (msgend < p+4)
+               goto failure;
+           b.len = toint(GET_32BIT(p));
+           p += 4;
+
+           if (b.len < 0 || b.len > msgend - p)
+               goto failure;
+           b.blob = p;
+           p += b.len;
+
+           key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
+           if (!key)
+               goto failure;
+
+           PUT_32BIT(ret, 1);
+           ret[4] = SSH_AGENT_FAILURE;
+           if (key) {
+               del234(ssh2keys, key);
+               keylist_update();
+               key->alg->freekey(key->data);
+               sfree(key);
+               ret[4] = SSH_AGENT_SUCCESS;
+           }
+       }
+       break;
+      case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
+       /*
+        * Remove all SSH-1 keys. Always returns success.
+        */
+       {
+           struct RSAKey *rkey;
+
+           while ((rkey = index234(rsakeys, 0)) != NULL) {
+               del234(rsakeys, rkey);
+               freersakey(rkey);
+               sfree(rkey);
+           }
+           keylist_update();
+
+           PUT_32BIT(ret, 1);
+           ret[4] = SSH_AGENT_SUCCESS;
+       }
+       break;
+      case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
+       /*
+        * Remove all SSH-2 keys. Always returns success.
+        */
+       {
+           struct ssh2_userkey *skey;
+
+           while ((skey = index234(ssh2keys, 0)) != NULL) {
+               del234(ssh2keys, skey);
+               skey->alg->freekey(skey->data);
+               sfree(skey);
+           }
+           keylist_update();
+
+           PUT_32BIT(ret, 1);
+           ret[4] = SSH_AGENT_SUCCESS;
+       }
+       break;
+      default:
+      failure:
+       /*
+        * Unrecognised message. Return SSH_AGENT_FAILURE.
+        */
+       PUT_32BIT(ret, 1);
+       ret[4] = SSH_AGENT_FAILURE;
+       break;
+    }
+
+    *outlen = 4 + GET_32BIT(ret);
+    return ret;
+}
+
+void *pageant_failure_msg(int *outlen)
+{
+    unsigned char *ret = snewn(5, unsigned char);
+    PUT_32BIT(ret, 1);
+    ret[4] = SSH_AGENT_FAILURE;
+    *outlen = 5;
+    return ret;
+}
+
+void pageant_init(void)
+{
+    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)
+{
+    return add234(rsakeys, rkey) != rkey;
+}
+
+int pageant_add_ssh2_key(struct ssh2_userkey *skey)
+{
+    return add234(ssh2keys, skey) != skey;
+}
+
+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;
+}
diff --git a/pageant.h b/pageant.h
new file mode 100644 (file)
index 0000000..6ea13ef
--- /dev/null
+++ b/pageant.h
@@ -0,0 +1,67 @@
+/*
+ * pageant.h: header for pageant.c.
+ */
+
+/*
+ * FIXME: it would be nice not to have this arbitrary limit. It's
+ * currently needed because the Windows Pageant IPC system needs an
+ * upper bound known to the client, but it's also reused as a basic
+ * sanity check on incoming messages' length fields.
+ */
+#define AGENT_MAX_MSGLEN  8192
+
+/*
+ * Initial setup.
+ */
+void pageant_init(void);
+
+/*
+ * The main agent function that answers messages.
+ *
+ * Expects a message/length pair as input, minus its initial length
+ * field but still with its type code on the front.
+ *
+ * Returns a fully formatted message as output, *with* its initial
+ * length field, and sets *outlen to the full size of that message.
+ */
+void *pageant_handle_msg(const void *msg, int msglen, int *outlen);
+
+/*
+ * Construct a failure response. Useful for agent front ends which
+ * suffer a problem before they even get to pageant_handle_msg.
+ */
+void *pageant_failure_msg(int *outlen);
+
+/*
+ * Construct a list of public keys, just as the two LIST_IDENTITIES
+ * requests would have returned them.
+ */
+void *pageant_make_keylist1(int *length);
+void *pageant_make_keylist2(int *length);
+
+/*
+ * Accessor functions for Pageant's internal key lists. Fetch the nth
+ * key; count the keys; attempt to add a key (returning true on
+ * success, in which case the ownership of the key structure has been
+ * taken over by pageant.c); attempt to delete a key (returning true
+ * on success, in which case the ownership of the key structure is
+ * passed back to the client).
+ */
+struct RSAKey *pageant_nth_ssh1_key(int i);
+struct ssh2_userkey *pageant_nth_ssh2_key(int i);
+int pageant_count_ssh1_keys(void);
+int pageant_count_ssh2_keys(void);
+int pageant_add_ssh1_key(struct RSAKey *rkey);
+int pageant_add_ssh2_key(struct ssh2_userkey *skey);
+int pageant_delete_ssh1_key(struct RSAKey *rkey);
+int pageant_delete_ssh2_key(struct ssh2_userkey *skey);
+
+/*
+ * This callback must be provided by the Pageant front end code.
+ * pageant_handle_msg calls it to indicate that the message it's just
+ * handled has changed the list of keys held by the agent. Front ends
+ * which expose that key list through dedicated UI may need to refresh
+ * that UI's state in this function; other front ends can leave it
+ * empty.
+ */
+void keylist_update(void);
index 828747dac62a83bdb391f616d53c934bc510f730..b643bdef8777dc9d26af25921d11fe78c8f0eeba 100644 (file)
@@ -15,6 +15,7 @@
 #include "misc.h"
 #include "tree234.h"
 #include "winsecur.h"
+#include "pageant.h"
 
 #include <shellapi.h>
 
 
 #define AGENT_COPYDATA_ID 0x804e50ba   /* random goop */
 
-/*
- * FIXME: maybe some day we can sort this out ...
- */
-#define AGENT_MAX_MSGLEN  8192
-
 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
  * wParam are used by Windows, and should be masked off, so we shouldn't
  * attempt to store information in them. Hence all these identifiers have
@@ -114,46 +110,14 @@ static void unmungestr(char *in, char *out, int outlen)
     return;
 }
 
-static tree234 *rsakeys, *ssh2keys;
-
 static int has_security;
 
 /*
  * Forward references
  */
-static void *make_keylist1(int *length);
-static void *make_keylist2(int *length);
 static void *get_keylist1(int *length);
 static void *get_keylist2(int *length);
 
-/*
- * We need this to link with the RSA code, because rsaencrypt()
- * pads its data with random bytes. Since we only use rsadecrypt()
- * 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)
-{
-    MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
-    exit(0);
-    /* this line can't be reached but it placates MSVC's warnings :-) */
-    return 0;
-}
-
-/*
- * Blob structure for passing to the asymmetric SSH-2 key compare
- * function, prototyped here.
- */
-struct blob {
-    unsigned char *blob;
-    int len;
-};
-static int cmpkeys_ssh2_asymm(void *av, void *bv);
-
 struct PassphraseProcStruct {
     char **passphrase;
     char *comment;
@@ -321,7 +285,7 @@ void old_keyfile_warning(void)
 /*
  * Update the visible key list.
  */
-static void keylist_update(void)
+void keylist_update(void)
 {
     struct RSAKey *rkey;
     struct ssh2_userkey *skey;
@@ -329,7 +293,7 @@ static void keylist_update(void)
 
     if (keylist) {
        SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
-       for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
+       for (i = 0; NULL != (rkey = pageant_nth_ssh1_key(i)); i++) {
            char listentry[512], *p;
            /*
             * Replace two spaces in the fingerprint with tabs, for
@@ -347,7 +311,7 @@ static void keylist_update(void)
            SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
                               0, (LPARAM) listentry);
        }
-       for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
+       for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) {
            char *listentry, *p;
            int pos, fp_len;
            /*
@@ -639,7 +603,7 @@ static void add_keyfile(Filename *filename)
            sfree(request);
            sfree(response);
        } else {
-           if (add234(rsakeys, rkey) != rkey)
+           if (!pageant_add_ssh1_key(rkey))
                sfree(rkey);           /* already present, don't waste RAM */
        }
     } else {
@@ -686,7 +650,7 @@ static void add_keyfile(Filename *filename)
            sfree(request);
            sfree(response);
        } else {
-           if (add234(ssh2keys, skey) != skey) {
+           if (!pageant_add_ssh2_key(skey)) {
                skey->alg->freekey(skey->data);
                sfree(skey);           /* already present, don't waste RAM */
            }
@@ -694,105 +658,10 @@ static void add_keyfile(Filename *filename)
     }
 }
 
-/*
- * Create an SSH-1 key list in a malloc'ed buffer; return its
- * length.
- */
-static void *make_keylist1(int *length)
-{
-    int i, nkeys, len;
-    struct RSAKey *key;
-    unsigned char *blob, *p, *ret;
-    int bloblen;
-
-    /*
-     * Count up the number and length of keys we hold.
-     */
-    len = 4;
-    nkeys = 0;
-    for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
-       nkeys++;
-       blob = rsa_public_blob(key, &bloblen);
-       len += bloblen;
-       sfree(blob);
-       len += 4 + strlen(key->comment);
-    }
-
-    /* Allocate the buffer. */
-    p = ret = snewn(len, unsigned char);
-    if (length) *length = len;
-
-    PUT_32BIT(p, nkeys);
-    p += 4;
-    for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
-       blob = rsa_public_blob(key, &bloblen);
-       memcpy(p, blob, bloblen);
-       p += bloblen;
-       sfree(blob);
-       PUT_32BIT(p, strlen(key->comment));
-       memcpy(p + 4, key->comment, strlen(key->comment));
-       p += 4 + strlen(key->comment);
-    }
-
-    assert(p - ret == len);
-    return ret;
-}
-
-/*
- * Create an SSH-2 key list in a malloc'ed buffer; return its
- * length.
- */
-static void *make_keylist2(int *length)
-{
-    struct ssh2_userkey *key;
-    int i, len, nkeys;
-    unsigned char *blob, *p, *ret;
-    int bloblen;
-
-    /*
-     * Count up the number and length of keys we hold.
-     */
-    len = 4;
-    nkeys = 0;
-    for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
-       nkeys++;
-       len += 4;              /* length field */
-       blob = key->alg->public_blob(key->data, &bloblen);
-       len += bloblen;
-       sfree(blob);
-       len += 4 + strlen(key->comment);
-    }
-
-    /* Allocate the buffer. */
-    p = ret = snewn(len, unsigned char);
-    if (length) *length = len;
-
-    /*
-     * Packet header is the obvious five bytes, plus four
-     * bytes for the key count.
-     */
-    PUT_32BIT(p, nkeys);
-    p += 4;
-    for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
-       blob = key->alg->public_blob(key->data, &bloblen);
-       PUT_32BIT(p, bloblen);
-       p += 4;
-       memcpy(p, blob, bloblen);
-       p += bloblen;
-       sfree(blob);
-       PUT_32BIT(p, strlen(key->comment));
-       memcpy(p + 4, key->comment, strlen(key->comment));
-       p += 4 + strlen(key->comment);
-    }
-
-    assert(p - ret == len);
-    return ret;
-}
-
 /*
  * Acquire a keylist1 from the primary Pageant; this means either
- * calling make_keylist1 (if that's us) or sending a message to the
- * primary Pageant (if it's not).
+ * calling pageant_make_keylist1 (if that's us) or sending a message
+ * to the primary Pageant (if it's not).
  */
 static void *get_keylist1(int *length)
 {
@@ -820,15 +689,15 @@ static void *get_keylist1(int *length)
        if (length)
            *length = resplen-5;
     } else {
-       ret = make_keylist1(length);
+       ret = pageant_make_keylist1(length);
     }
     return ret;
 }
 
 /*
  * Acquire a keylist2 from the primary Pageant; this means either
- * calling make_keylist2 (if that's us) or sending a message to the
- * primary Pageant (if it's not).
+ * calling pageant_make_keylist2 (if that's us) or sending a message
+ * to the primary Pageant (if it's not).
  */
 static void *get_keylist2(int *length)
 {
@@ -857,575 +726,37 @@ static void *get_keylist2(int *length)
        if (length)
            *length = resplen-5;
     } else {
-       ret = make_keylist2(length);
+       ret = pageant_make_keylist2(length);
     }
     return ret;
 }
 
-/*
- * This is the main agent function that answers messages.
- */
-static void answer_msg(void *msg)
-{
-    unsigned char *p = msg;
-    unsigned char *ret = msg;
-    unsigned char *msgend;
-    int type;
-
-    /*
-     * Get the message length.
-     */
-    msgend = p + 4 + GET_32BIT(p);
-
-    /*
-     * Get the message type.
-     */
-    if (msgend < p+5)
-       goto failure;
-    type = p[4];
-
-    p += 5;
-    switch (type) {
-      case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
-       /*
-        * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
-        */
-       {
-           int len;
-           void *keylist;
-
-           ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
-           keylist = make_keylist1(&len);
-           if (len + 5 > AGENT_MAX_MSGLEN) {
-               sfree(keylist);
-               goto failure;
-           }
-           PUT_32BIT(ret, len + 1);
-           memcpy(ret + 5, keylist, len);
-           sfree(keylist);
-       }
-       break;
-      case SSH2_AGENTC_REQUEST_IDENTITIES:
-       /*
-        * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
-        */
-       {
-           int len;
-           void *keylist;
-
-           ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
-           keylist = make_keylist2(&len);
-           if (len + 5 > AGENT_MAX_MSGLEN) {
-               sfree(keylist);
-               goto failure;
-           }
-           PUT_32BIT(ret, len + 1);
-           memcpy(ret + 5, keylist, len);
-           sfree(keylist);
-       }
-       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;
-           int i, len;
-
-           p += 4;
-           i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
-           if (i < 0)
-               goto failure;
-           p += i;
-           i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
-           if (i < 0) {
-                freebn(reqkey.exponent);
-               goto failure;
-            }
-           p += i;
-           i = ssh1_read_bignum(p, msgend - p, &challenge);
-           if (i < 0) {
-                freebn(reqkey.exponent);
-                freebn(reqkey.modulus);
-               goto failure;
-            }
-           p += i;
-           if (msgend < p+16) {
-               freebn(reqkey.exponent);
-               freebn(reqkey.modulus);
-               freebn(challenge);
-               goto failure;
-           }
-           memcpy(response_source + 32, p, 16);
-           p += 16;
-           if (msgend < p+4 ||
-               GET_32BIT(p) != 1 ||
-               (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
-               freebn(reqkey.exponent);
-               freebn(reqkey.modulus);
-               freebn(challenge);
-               goto failure;
-           }
-           response = rsadecrypt(challenge, key);
-           for (i = 0; i < 32; i++)
-               response_source[i] = bignum_byte(response, 31 - i);
-
-           MD5Init(&md5c);
-           MD5Update(&md5c, response_source, 48);
-           MD5Final(response_md5, &md5c);
-           smemclr(response_source, 48);       /* burn the evidence */
-           freebn(response);          /* and that evidence */
-           freebn(challenge);         /* yes, and that evidence */
-           freebn(reqkey.exponent);   /* and free some memory ... */
-           freebn(reqkey.modulus);    /* ... while we're at it. */
-
-           /*
-            * Packet is the obvious five byte header, plus sixteen
-            * bytes of MD5.
-            */
-           len = 5 + 16;
-           PUT_32BIT(ret, len - 4);
-           ret[4] = SSH1_AGENT_RSA_RESPONSE;
-           memcpy(ret + 5, response_md5, 16);
-       }
-       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;
-           struct blob b;
-           unsigned char *data, *signature;
-           int datalen, siglen, len;
-
-           if (msgend < p+4)
-               goto failure;
-           b.len = toint(GET_32BIT(p));
-            if (b.len < 0 || b.len > msgend - (p+4))
-                goto failure;
-           p += 4;
-           b.blob = p;
-           p += b.len;
-           if (msgend < p+4)
-               goto failure;
-           datalen = toint(GET_32BIT(p));
-           p += 4;
-           if (datalen < 0 || datalen > msgend - p)
-               goto failure;
-           data = p;
-           key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
-           if (!key)
-               goto failure;
-           signature = key->alg->sign(key->data, data, datalen, &siglen);
-           len = 5 + 4 + siglen;
-           PUT_32BIT(ret, len - 4);
-           ret[4] = SSH2_AGENT_SIGN_RESPONSE;
-           PUT_32BIT(ret + 5, siglen);
-           memcpy(ret + 5 + 4, signature, siglen);
-           sfree(signature);
-       }
-       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;
-
-           key = snew(struct RSAKey);
-           memset(key, 0, sizeof(struct RSAKey));
-
-           n = makekey(p, msgend - p, key, NULL, 1);
-           if (n < 0) {
-               freersakey(key);
-               sfree(key);
-               goto failure;
-           }
-           p += n;
-
-           n = makeprivate(p, msgend - p, key);
-           if (n < 0) {
-               freersakey(key);
-               sfree(key);
-               goto failure;
-           }
-           p += n;
-
-           n = ssh1_read_bignum(p, msgend - p, &key->iqmp);  /* p^-1 mod q */
-           if (n < 0) {
-               freersakey(key);
-               sfree(key);
-               goto failure;
-           }
-           p += n;
-
-           n = ssh1_read_bignum(p, msgend - p, &key->p);  /* p */
-           if (n < 0) {
-               freersakey(key);
-               sfree(key);
-               goto failure;
-           }
-           p += n;
-
-           n = ssh1_read_bignum(p, msgend - p, &key->q);  /* q */
-           if (n < 0) {
-               freersakey(key);
-               sfree(key);
-               goto failure;
-           }
-           p += n;
-
-           if (msgend < p+4) {
-               freersakey(key);
-               sfree(key);
-               goto failure;
-           }
-            commentlen = toint(GET_32BIT(p));
-
-           if (commentlen < 0 || commentlen > msgend - p) {
-               freersakey(key);
-               sfree(key);
-               goto failure;
-           }
-
-           comment = snewn(commentlen+1, char);
-           if (comment) {
-               memcpy(comment, p + 4, commentlen);
-                comment[commentlen] = '\0';
-               key->comment = comment;
-           }
-           PUT_32BIT(ret, 1);
-           ret[4] = SSH_AGENT_FAILURE;
-           if (add234(rsakeys, key) == key) {
-               keylist_update();
-               ret[4] = SSH_AGENT_SUCCESS;
-           } else {
-               freersakey(key);
-               sfree(key);
-           }
-       }
-       break;
-      case SSH2_AGENTC_ADD_IDENTITY:
-       /*
-        * Add to the list and return SSH_AGENT_SUCCESS, or
-        * SSH_AGENT_FAILURE if the key was malformed.
-        */
-       {
-           struct ssh2_userkey *key;
-           char *comment, *alg;
-           int alglen, commlen;
-           int bloblen;
-
-
-           if (msgend < p+4)
-               goto failure;
-           alglen = toint(GET_32BIT(p));
-           p += 4;
-           if (alglen < 0 || alglen > msgend - p)
-               goto failure;
-           alg = p;
-           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 {
-               sfree(key);
-               goto failure;
-           }
-
-           bloblen = msgend - p;
-           key->data = key->alg->openssh_createkey(&p, &bloblen);
-           if (!key->data) {
-               sfree(key);
-               goto failure;
-           }
-
-           /*
-            * p has been advanced by openssh_createkey, but
-            * certainly not _beyond_ the end of the buffer.
-            */
-           assert(p <= msgend);
-
-           if (msgend < p+4) {
-               key->alg->freekey(key->data);
-               sfree(key);
-               goto failure;
-           }
-           commlen = toint(GET_32BIT(p));
-           p += 4;
-
-           if (commlen < 0 || commlen > msgend - p) {
-               key->alg->freekey(key->data);
-               sfree(key);
-               goto failure;
-           }
-           comment = snewn(commlen + 1, char);
-           if (comment) {
-               memcpy(comment, p, commlen);
-               comment[commlen] = '\0';
-           }
-           key->comment = comment;
-
-           PUT_32BIT(ret, 1);
-           ret[4] = SSH_AGENT_FAILURE;
-           if (add234(ssh2keys, key) == key) {
-               keylist_update();
-               ret[4] = SSH_AGENT_SUCCESS;
-           } else {
-               key->alg->freekey(key->data);
-               sfree(key->comment);
-               sfree(key);
-           }
-       }
-       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;
-
-           n = makekey(p, msgend - p, &reqkey, NULL, 0);
-           if (n < 0)
-               goto failure;
-
-           key = find234(rsakeys, &reqkey, NULL);
-           freebn(reqkey.exponent);
-           freebn(reqkey.modulus);
-           PUT_32BIT(ret, 1);
-           ret[4] = SSH_AGENT_FAILURE;
-           if (key) {
-               del234(rsakeys, key);
-               keylist_update();
-               freersakey(key);
-               sfree(key);
-               ret[4] = SSH_AGENT_SUCCESS;
-           }
-       }
-       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;
-           struct blob b;
-
-           if (msgend < p+4)
-               goto failure;
-           b.len = toint(GET_32BIT(p));
-           p += 4;
-
-           if (b.len < 0 || b.len > msgend - p)
-               goto failure;
-           b.blob = p;
-           p += b.len;
-
-           key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
-           if (!key)
-               goto failure;
-
-           PUT_32BIT(ret, 1);
-           ret[4] = SSH_AGENT_FAILURE;
-           if (key) {
-               del234(ssh2keys, key);
-               keylist_update();
-               key->alg->freekey(key->data);
-               sfree(key);
-               ret[4] = SSH_AGENT_SUCCESS;
-           }
-       }
-       break;
-      case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
-       /*
-        * Remove all SSH-1 keys. Always returns success.
-        */
-       {
-           struct RSAKey *rkey;
-
-           while ((rkey = index234(rsakeys, 0)) != NULL) {
-               del234(rsakeys, rkey);
-               freersakey(rkey);
-               sfree(rkey);
-           }
-           keylist_update();
-
-           PUT_32BIT(ret, 1);
-           ret[4] = SSH_AGENT_SUCCESS;
-       }
-       break;
-      case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
-       /*
-        * Remove all SSH-2 keys. Always returns success.
-        */
-       {
-           struct ssh2_userkey *skey;
-
-           while ((skey = index234(ssh2keys, 0)) != NULL) {
-               del234(ssh2keys, skey);
-               skey->alg->freekey(skey->data);
-               sfree(skey);
-           }
-           keylist_update();
-
-           PUT_32BIT(ret, 1);
-           ret[4] = SSH_AGENT_SUCCESS;
-       }
-       break;
-      default:
-      failure:
-       /*
-        * Unrecognised message. Return SSH_AGENT_FAILURE.
-        */
-       PUT_32BIT(ret, 1);
-       ret[4] = SSH_AGENT_FAILURE;
-       break;
-    }
-}
-
-/*
- * Key comparison function for the 2-3-4 tree of RSA keys.
- */
-static int cmpkeys_rsa(void *av, void *bv)
+static void answer_msg(void *msgv)
 {
-    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;
-}
-
-/*
- * Key comparison function for the 2-3-4 tree of SSH-2 keys.
- */
-static int cmpkeys_ssh2(void *av, void *bv)
-{
-    struct ssh2_userkey *a = (struct ssh2_userkey *) av;
-    struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
-    int i;
-    int alen, blen;
-    unsigned char *ablob, *bblob;
-    int c;
-
-    /*
-     * Compare purely by public blob.
-     */
-    ablob = a->alg->public_blob(a->data, &alen);
-    bblob = b->alg->public_blob(b->data, &blen);
-
-    c = 0;
-    for (i = 0; i < alen && i < blen; i++) {
-       if (ablob[i] < bblob[i]) {
-           c = -1;
-           break;
-       } else if (ablob[i] > bblob[i]) {
-           c = +1;
-           break;
-       }
+    unsigned char *msg = (unsigned char *)msgv;
+    unsigned msglen;
+    void *reply;
+    int replylen;
+
+    msglen = GET_32BIT(msg);
+    if (msglen > AGENT_MAX_MSGLEN) {
+        reply = pageant_failure_msg(&replylen);
+    } else {
+        reply = pageant_handle_msg(msg + 4, msglen, &replylen);
+        if (replylen > AGENT_MAX_MSGLEN) {
+            smemclr(reply, replylen);
+            sfree(reply);
+            reply = pageant_failure_msg(&replylen);
+        }
     }
-    if (c == 0 && i < alen)
-       c = +1;                        /* a is longer */
-    if (c == 0 && i < blen)
-       c = -1;                        /* a is longer */
-
-    sfree(ablob);
-    sfree(bblob);
-
-    return c;
-}
-
-/*
- * Key comparison function for looking up a blob in the 2-3-4 tree
- * of SSH-2 keys.
- */
-static int cmpkeys_ssh2_asymm(void *av, void *bv)
-{
-    struct blob *a = (struct blob *) av;
-    struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
-    int i;
-    int alen, blen;
-    unsigned char *ablob, *bblob;
-    int c;
 
     /*
-     * Compare purely by public blob.
+     * Windows Pageant answers messages in place, by overwriting the
+     * input message buffer.
      */
-    ablob = a->blob;
-    alen = a->len;
-    bblob = b->alg->public_blob(b->data, &blen);
-
-    c = 0;
-    for (i = 0; i < alen && i < blen; i++) {
-       if (ablob[i] < bblob[i]) {
-           c = -1;
-           break;
-       } else if (ablob[i] > bblob[i]) {
-           c = +1;
-           break;
-       }
-    }
-    if (c == 0 && i < alen)
-       c = +1;                        /* a is longer */
-    if (c == 0 && i < blen)
-       c = -1;                        /* a is longer */
-
-    sfree(bblob);
-
-    return c;
+    memcpy(msg, reply, replylen);
+    smemclr(reply, replylen);
+    sfree(reply);
 }
 
 /*
@@ -1567,35 +898,35 @@ static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
                                numSelected, (WPARAM)selectedArray);
                
                itemNum = numSelected - 1;
-               rCount = count234(rsakeys);
-               sCount = count234(ssh2keys);
+               rCount = pageant_count_ssh1_keys();
+               sCount = pageant_count_ssh2_keys();
                
                /* go through the non-rsakeys until we've covered them all, 
                 * and/or we're out of selected items to check. note that
                 * we go *backwards*, to avoid complications from deleting
                 * things hence altering the offset of subsequent items
                 */
-           for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
-                       skey = index234(ssh2keys, i);
+                for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
+                    skey = pageant_nth_ssh2_key(i);
                        
-                       if (selectedArray[itemNum] == rCount + i) {
-                               del234(ssh2keys, skey);
-                               skey->alg->freekey(skey->data);
-                               sfree(skey);
-                               itemNum--; 
-                       }
+                    if (selectedArray[itemNum] == rCount + i) {
+                        pageant_delete_ssh2_key(skey);
+                        skey->alg->freekey(skey->data);
+                        sfree(skey);
+                        itemNum--;
+                    }
                }
                
                /* do the same for the rsa keys */
                for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
-                       rkey = index234(rsakeys, i);
-
-                       if(selectedArray[itemNum] == i) {
-                               del234(rsakeys, rkey);
-                               freersakey(rkey);
-                               sfree(rkey);
-                               itemNum--;
-                       }
+                    rkey = pageant_nth_ssh1_key(i);
+
+                    if(selectedArray[itemNum] == i) {
+                        pageant_delete_ssh1_key(rkey);
+                        freersakey(rkey);
+                        sfree(rkey);
+                        itemNum--;
+                    }
                }
 
                sfree(selectedArray); 
@@ -2116,11 +1447,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
     already_running = agent_exists();
 
     /*
-     * Initialise storage for RSA keys.
+     * Initialise the cross-platform Pageant code.
      */
     if (!already_running) {
-       rsakeys = newtree234(cmpkeys_rsa);
-       ssh2keys = newtree234(cmpkeys_ssh2);
+        pageant_init();
     }
 
     /*