--- /dev/null
+/*
+ * 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;
+}
#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
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;
/*
* Update the visible key list.
*/
-static void keylist_update(void)
+void keylist_update(void)
{
struct RSAKey *rkey;
struct ssh2_userkey *skey;
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
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;
/*
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 {
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 */
}
}
}
-/*
- * 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)
{
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)
{
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);
}
/*
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);
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();
}
/*