#endif
/*
- * Exports from pageantc.c
- */
-void agent_query(void *in, int inlen, void **out, int *outlen);
-int agent_exists(void);
-
-/*
* Forward references
*/
static void *make_keylist1(int *length);
if (already_running) {
unsigned char *request, *response;
void *vresponse;
- int reqlen, clen, resplen;
+ int reqlen, clen, resplen, ret;
clen = strlen(rkey->comment);
reqlen += 4 + clen;
PUT_32BIT(request, reqlen - 4);
- agent_query(request, reqlen, &vresponse, &resplen);
+ ret = agent_query(request, reqlen, &vresponse, &resplen,
+ NULL, NULL);
+ assert(ret == 1);
response = vresponse;
if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
MessageBox(NULL, "The already running Pageant "
if (already_running) {
unsigned char *request, *response;
void *vresponse;
- int reqlen, alglen, clen, keybloblen, resplen;
+ int reqlen, alglen, clen, keybloblen, resplen, ret;
alglen = strlen(skey->alg->name);
clen = strlen(skey->comment);
PUT_32BIT(request, reqlen - 4);
reqlen += clen + 4;
- agent_query(request, reqlen, &vresponse, &resplen);
+ ret = agent_query(request, reqlen, &vresponse, &resplen,
+ NULL, NULL);
+ assert(ret == 1);
response = vresponse;
if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
MessageBox(NULL, "The already running Pageant "
if (already_running) {
unsigned char request[5], *response;
void *vresponse;
- int resplen;
+ int resplen, retval;
request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
PUT_32BIT(request, 4);
- agent_query(request, 5, &vresponse, &resplen);
+ retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
+ assert(retval == 1);
response = vresponse;
if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER)
return NULL;
if (already_running) {
unsigned char request[5], *response;
void *vresponse;
- int resplen;
+ int resplen, retval;
request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
PUT_32BIT(request, 4);
- agent_query(request, 5, &vresponse, &resplen);
+ retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
+ assert(retval == 1);
response = vresponse;
if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER)
return NULL;
return TRUE;
}
-void agent_query(void *in, int inlen, void **out, int *outlen)
+int agent_query(void *in, int inlen, void **out, int *outlen,
+ void (*callback)(void *, void *, int), void *callback_ctx)
{
HWND hwnd;
char mapname[64];
hwnd = FindWindow("Pageant", "Pageant");
debug(("hwnd is %p\n", hwnd));
if (!hwnd)
- return;
+ return 1; /* *out == NULL, so failure */
sprintf(mapname, "PageantRequest%08x", (unsigned)GetCurrentThreadId());
filemap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
0, AGENT_MAX_MSGLEN, mapname);
if (!filemap)
- return;
+ return 1; /* *out == NULL, so failure */
p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
memcpy(p, in, inlen);
cds.dwData = AGENT_COPYDATA_ID;
}
UnmapViewOfFile(p);
CloseHandle(filemap);
+
+ return 1;
}
#ifdef TESTMODE
#endif
/*
- * Exports from pageantc.c
+ * Exports from pageantc.c.
+ *
+ * agent_query returns 1 for here's-a-response, and 0 for query-in-
+ * progress. In the latter case there will be a call to `callback'
+ * at some future point, passing callback_ctx as the first
+ * parameter and the actual reply data as the second and third.
+ *
+ * The response may be a NULL pointer (in either of the synchronous
+ * or asynchronous cases), which indicates failure to receive a
+ * response.
*/
-void agent_query(void *in, int inlen, void **out, int *outlen);
+int agent_query(void *in, int inlen, void **out, int *outlen,
+ void (*callback)(void *, void *, int), void *callback_ctx);
int agent_exists(void);
/*
static void ssh2_pkt_addmp(Ssh, Bignum b);
static int ssh2_pkt_construct(Ssh);
static void ssh2_pkt_send(Ssh);
+static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt);
+static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt);
/*
* Buffer management constants. There are several of these for
* potentially reconfigure port forwardings etc in mid-session.
*/
Config cfg;
+
+ /*
+ * Used to transfer data back from async agent callbacks.
+ */
+ void *agent_response;
+ int agent_response_len;
};
#define logevent(s) logevent(ssh->frontend, s)
return 0;
}
+void ssh_agent_callback(void *sshv, void *reply, int replylen)
+{
+ Ssh ssh = (Ssh) sshv;
+
+ ssh->agent_response = reply;
+ ssh->agent_response_len = replylen;
+
+ if (ssh->version == 1)
+ do_ssh1_login(ssh, NULL, -1, 0);
+ else
+ do_ssh2_authconn(ssh, NULL, -1, 0);
+}
+
+void ssh_agentf_callback(void *cv, void *reply, int replylen)
+{
+ struct ssh_channel *c = (struct ssh_channel *)cv;
+ Ssh ssh = c->ssh;
+ void *sentreply = reply;
+
+ if (!sentreply) {
+ /* Fake SSH_AGENT_FAILURE. */
+ sentreply = "\0\0\0\1\5";
+ replylen = 5;
+ }
+ if (ssh->version == 2) {
+ ssh2_add_channel_data(c, sentreply, replylen);
+ ssh2_try_send(c);
+ } else {
+ send_packet(ssh, SSH1_MSG_CHANNEL_DATA,
+ PKT_INT, c->remoteid,
+ PKT_INT, replylen,
+ PKT_DATA, sentreply, replylen,
+ PKT_END);
+ }
+ if (reply)
+ sfree(reply);
+}
+
/*
* Handle the key exchange and user authentication phases.
*/
/* Request the keys held by the agent. */
PUT_32BIT(s->request, 1);
s->request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
- agent_query(s->request, 5, &r, &s->responselen);
+ if (!agent_query(s->request, 5, &r, &s->responselen,
+ ssh_agent_callback, ssh)) {
+ do {
+ crReturn(0);
+ if (ispkt) {
+ bombout(("Unexpected data from server while waiting"
+ " for agent response"));
+ crStop(0);
+ }
+ } while (ispkt || inlen > 0);
+ r = ssh->agent_response;
+ s->responselen = ssh->agent_response_len;
+ }
s->response = (unsigned char *) r;
if (s->response && s->responselen >= 5 &&
s->response[4] == SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
memcpy(q, s->session_id, 16);
q += 16;
PUT_32BIT(q, 1); /* response format */
- agent_query(agentreq, len + 4, &vret, &retlen);
+ if (!agent_query(agentreq, len + 4, &vret, &retlen,
+ ssh_agent_callback, ssh)) {
+ sfree(agentreq);
+ do {
+ crReturn(0);
+ if (ispkt) {
+ bombout(("Unexpected data from server"
+ " while waiting for agent"
+ " response"));
+ crStop(0);
+ }
+ } while (ispkt || inlen > 0);
+ vret = ssh->agent_response;
+ retlen = ssh->agent_response_len;
+ } else
+ sfree(agentreq);
ret = vret;
- sfree(agentreq);
if (ret) {
if (ret[4] == SSH1_AGENT_RSA_RESPONSE) {
logevent("Sending Pageant's response");
c->u.a.lensofar += l;
}
if (c->u.a.lensofar == c->u.a.totallen) {
- void *reply, *sentreply;
+ void *reply;
int replylen;
- agent_query(c->u.a.message,
- c->u.a.totallen, &reply,
- &replylen);
- if (reply)
- sentreply = reply;
- else {
- /* Fake SSH_AGENT_FAILURE. */
- sentreply = "\0\0\0\1\5";
- replylen = 5;
- }
- send_packet(ssh, SSH1_MSG_CHANNEL_DATA,
- PKT_INT, c->remoteid,
- PKT_INT, replylen,
- PKT_DATA, sentreply, replylen,
- PKT_END);
- if (reply)
- sfree(reply);
+ if (agent_query(c->u.a.message,
+ c->u.a.totallen,
+ &reply, &replylen,
+ ssh_agentf_callback, c))
+ ssh_agentf_callback(c, reply, replylen);
sfree(c->u.a.message);
c->u.a.lensofar = 0;
}
/* Request the keys held by the agent. */
PUT_32BIT(s->request, 1);
s->request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
- agent_query(s->request, 5, &r, &s->responselen);
+ if (!agent_query(s->request, 5, &r, &s->responselen,
+ ssh_agent_callback, ssh)) {
+ do {
+ crReturnV;
+ if (ispkt) {
+ bombout(("Unexpected data from server while"
+ " waiting for agent response"));
+ crStopV;
+ }
+ } while (ispkt || inlen > 0);
+ r = ssh->agent_response;
+ s->responselen = ssh->agent_response_len;
+ }
s->response = (unsigned char *) r;
if (s->response && s->responselen >= 5 &&
s->response[4] == SSH2_AGENT_IDENTITIES_ANSWER) {
s->q += ssh->pktout.length - 5;
/* And finally the (zero) flags word. */
PUT_32BIT(s->q, 0);
- agent_query(s->agentreq, s->len + 4, &vret, &s->retlen);
+ if (!agent_query(s->agentreq, s->len + 4,
+ &vret, &s->retlen,
+ ssh_agent_callback, ssh)) {
+ do {
+ crReturnV;
+ if (ispkt) {
+ bombout(("Unexpected data from server"
+ " while waiting for agent"
+ " response"));
+ crStopV;
+ }
+ } while (ispkt || inlen > 0);
+ vret = ssh->agent_response;
+ s->retlen = ssh->agent_response_len;
+ }
s->ret = vret;
sfree(s->agentreq);
if (s->ret) {
c->u.a.lensofar += l;
}
if (c->u.a.lensofar == c->u.a.totallen) {
- void *reply, *sentreply;
+ void *reply;
int replylen;
- agent_query(c->u.a.message,
- c->u.a.totallen, &reply,
- &replylen);
- if (reply)
- sentreply = reply;
- else {
- /* Fake SSH_AGENT_FAILURE. */
- sentreply = "\0\0\0\1\5";
- replylen = 5;
- }
- ssh2_add_channel_data(c, sentreply, replylen);
- s->try_send = TRUE;
- if (reply)
- sfree(reply);
+ if (agent_query(c->u.a.message,
+ c->u.a.totallen,
+ &reply, &replylen,
+ ssh_agentf_callback, c))
+ ssh_agentf_callback(c, reply, replylen);
sfree(c->u.a.message);
c->u.a.lensofar = 0;
}
#include <sys/socket.h>
#include <sys/un.h>
+#include "putty.h"
#include "misc.h"
+#include "tree234.h"
#include "puttymem.h"
#define GET_32BIT(cp) \
return FALSE;
}
-void agent_query(void *in, int inlen, void **out, int *outlen)
+static tree234 *agent_connections;
+struct agent_connection {
+ int fd;
+ char *retbuf;
+ char sizebuf[4];
+ int retsize, retlen;
+ void (*callback)(void *, void *, int);
+ void *callback_ctx;
+};
+static int agent_conncmp(void *av, void *bv)
+{
+ struct agent_connection *a = (struct agent_connection *) av;
+ struct agent_connection *b = (struct agent_connection *) bv;
+ if (a->fd < b->fd)
+ return -1;
+ if (a->fd > b->fd)
+ return +1;
+ return 0;
+}
+static int agent_connfind(void *av, void *bv)
+{
+ int afd = *(int *) av;
+ struct agent_connection *b = (struct agent_connection *) bv;
+ if (afd < b->fd)
+ return -1;
+ if (afd > b->fd)
+ return +1;
+ return 0;
+}
+
+static int agent_select_result(int fd, int event)
+{
+ int ret;
+ struct agent_connection *conn;
+
+ assert(event == 1); /* not selecting for anything but R */
+
+ conn = find234(agent_connections, &fd, agent_connfind);
+ if (!conn) {
+ uxsel_del(fd);
+ return 1;
+ }
+
+ ret = read(fd, conn->retbuf+conn->retlen, conn->retsize-conn->retlen);
+ if (ret <= 0) {
+ if (conn->retbuf != conn->sizebuf) sfree(conn->retbuf);
+ conn->retbuf = NULL;
+ conn->retlen = 0;
+ goto done;
+ }
+ conn->retlen += ret;
+ if (conn->retsize == 4 && conn->retlen == 4) {
+ conn->retsize = GET_32BIT(conn->retbuf);
+ if (conn->retsize <= 0) {
+ conn->retbuf = NULL;
+ conn->retlen = 0;
+ goto done;
+ }
+ conn->retsize += 4;
+ assert(conn->retbuf == conn->sizebuf);
+ conn->retbuf = snewn(conn->retsize, char);
+ memcpy(conn->retbuf, conn->sizebuf, 4);
+ }
+
+ if (conn->retlen < conn->retsize)
+ return 0; /* more data to come */
+
+ done:
+ /*
+ * We have now completed the agent query. Do the callback, and
+ * clean up. (Of course we don't free retbuf, since ownership
+ * of that passes to the callback.)
+ */
+ conn->callback(conn->callback_ctx, conn->retbuf, conn->retlen);
+ uxsel_del(fd);
+ close(fd);
+ del234(agent_connections, conn);
+ sfree(conn);
+ return 0;
+}
+
+int agent_query(void *in, int inlen, void **out, int *outlen,
+ void (*callback)(void *, void *, int), void *callback_ctx)
{
char *name;
int sock;
struct sockaddr_un addr;
int done;
- int retsize, retlen;
- char sizebuf[4], *retbuf;
+ struct agent_connection *conn;
name = getenv("SSH_AUTH_SOCK");
if (!name)
done += ret;
}
- retbuf = sizebuf;
- retsize = 4;
- retlen = 0;
+ if (!agent_connections)
+ agent_connections = newtree234(agent_conncmp);
- while (retlen < retsize) {
- int ret = read(sock, retbuf + retlen, retsize - retlen);
- if (ret <= 0) {
- close(sock);
- if (retbuf != sizebuf) sfree(retbuf);
- goto failure;
- }
- retlen += ret;
- if (retsize == 4 && retlen == 4) {
- retsize = GET_32BIT(retbuf);
- if (retsize <= 0) {
- close(sock);
- goto failure;
- }
- retsize += 4;
- assert(retbuf == sizebuf);
- retbuf = snewn(retsize, char);
- memcpy(retbuf, sizebuf, 4);
- }
- }
+ conn = snew(struct agent_connection);
+ conn->fd = sock;
+ conn->retbuf = conn->sizebuf;
+ conn->retsize = 4;
+ conn->retlen = 0;
+ conn->callback = callback;
+ conn->callback_ctx = callback_ctx;
+ add234(agent_connections, conn);
- assert(retbuf != sizebuf);
- *out = retbuf;
- *outlen = retlen;
- return;
+ uxsel_set(sock, 1, agent_select_result);
+ return 0;
failure:
*out = NULL;
*outlen = 0;
+ return 1;
}