diff --git a/Recipe b/Recipe index 3b2db901..48cad133 100644 --- a/Recipe +++ b/Recipe @@ -244,7 +244,7 @@ NONSSH = telnet raw rlogin ldisc pinger SSH = ssh sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf + sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd + sshaes sshccp sshsh256 sshsh512 sshbn wildcard pinger ssharcf - + sshgssc pgssapi sshshare sshecc + + sshgssc pgssapi sshshare sshecc aqsync WINSSH = SSH winnoise wincapi winpgntc wingss winshare winnps winnpc + winhsock errsock UXSSH = SSH uxnoise uxagentc uxgss uxshare @@ -299,7 +299,7 @@ psftp : [C] psftp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC + psftp.res winnojmp LIBS pageant : [G] winpgnt pageant sshrsa sshpubk sshdes sshbn sshmd5 version - + tree234 misc sshaes sshsha winsecur winpgntc sshdss sshsh256 + + tree234 misc sshaes sshsha winsecur winpgntc aqsync sshdss sshsh256 + sshsh512 winutils sshecc winmisc winhelp conf pageant.res LIBS puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version @@ -331,10 +331,10 @@ cgtest : [UT] cgtest PUTTYGEN_UNIX pscp : [U] pscp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC psftp : [U] psftp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC -pageant : [X] uxpgnt uxagentc pageant sshrsa sshpubk sshdes sshbn sshmd5 - + version tree234 misc sshaes sshsha sshdss sshsh256 sshsh512 sshecc - + conf uxsignal nocproxy nogss be_none x11fwd ux_x11 uxcons gtkask - + gtkmisc UXMISC +pageant : [X] uxpgnt uxagentc aqsync pageant sshrsa sshpubk sshdes sshbn + + sshmd5 version tree234 misc sshaes sshsha sshdss sshsh256 sshsh512 + + sshecc conf uxsignal nocproxy nogss be_none x11fwd ux_x11 uxcons + + gtkask gtkmisc UXMISC ptermapp : [XT] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore + uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg diff --git a/aqsync.c b/aqsync.c new file mode 100644 index 00000000..e16eadd1 --- /dev/null +++ b/aqsync.c @@ -0,0 +1,21 @@ +/* + * aqsync.c: the agent_query_synchronous() wrapper function. + * + * This is a very small thing to have to put in its own module, but it + * wants to be shared between back ends, and exist in any SSH client + * program and also Pageant, and _nowhere else_ (because it pulls in + * the main agent_query). + */ + +#include + +#include "putty.h" + +void agent_query_synchronous(void *in, int inlen, void **out, int *outlen) +{ + agent_pending_query *pending; + + pending = agent_query(in, inlen, out, outlen, NULL, 0); + assert(!pending); +} + diff --git a/pageant.c b/pageant.c index c008f008..68bfab53 100644 --- a/pageant.c +++ b/pageant.c @@ -1196,12 +1196,12 @@ void *pageant_get_keylist1(int *length) if (!pageant_local) { unsigned char request[5], *response; void *vresponse; - int resplen, retval; + int resplen; + request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES; PUT_32BIT(request, 1); - retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL); - assert(retval == 1); + agent_query_synchronous(request, 5, &vresponse, &resplen); response = vresponse; if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) { sfree(response); @@ -1227,13 +1227,12 @@ void *pageant_get_keylist2(int *length) if (!pageant_local) { unsigned char request[5], *response; void *vresponse; - int resplen, retval; + int resplen; request[4] = SSH2_AGENTC_REQUEST_IDENTITIES; PUT_32BIT(request, 1); - retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL); - assert(retval == 1); + agent_query_synchronous(request, 5, &vresponse, &resplen); response = vresponse; if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) { sfree(response); @@ -1471,7 +1470,7 @@ int pageant_add_keyfile(Filename *filename, const char *passphrase, if (!pageant_local) { unsigned char *request, *response; void *vresponse; - int reqlen, clen, resplen, ret; + int reqlen, clen, resplen; clen = strlen(rkey->comment); @@ -1504,9 +1503,7 @@ int pageant_add_keyfile(Filename *filename, const char *passphrase, reqlen += 4 + clen; PUT_32BIT(request, reqlen - 4); - ret = agent_query(request, reqlen, &vresponse, &resplen, - NULL, NULL); - assert(ret == 1); + agent_query_synchronous(request, reqlen, &vresponse, &resplen); response = vresponse; if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) { *retstr = dupstr("The already running Pageant " @@ -1524,7 +1521,7 @@ int pageant_add_keyfile(Filename *filename, const char *passphrase, if (!pageant_local) { unsigned char *request, *response; void *vresponse; - int reqlen, alglen, clen, keybloblen, resplen, ret; + int reqlen, alglen, clen, keybloblen, resplen; alglen = strlen(skey->alg->name); clen = strlen(skey->comment); @@ -1552,9 +1549,7 @@ int pageant_add_keyfile(Filename *filename, const char *passphrase, reqlen += clen + 4; PUT_32BIT(request, reqlen - 4); - ret = agent_query(request, reqlen, &vresponse, &resplen, - NULL, NULL); - assert(ret == 1); + agent_query_synchronous(request, reqlen, &vresponse, &resplen); response = vresponse; if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) { *retstr = dupstr("The already running Pageant " @@ -1741,8 +1736,7 @@ int pageant_delete_key(struct pageant_pubkey *key, char **retstr) memcpy(request + 9, key->blob, key->bloblen); } - ret = agent_query(request, reqlen, &vresponse, &resplen, NULL, NULL); - assert(ret == 1); + agent_query_synchronous(request, reqlen, &vresponse, &resplen); response = vresponse; if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) { *retstr = dupstr("Agent failed to delete key"); @@ -1759,14 +1753,13 @@ int pageant_delete_key(struct pageant_pubkey *key, char **retstr) int pageant_delete_all_keys(char **retstr) { unsigned char request[5], *response; - int reqlen, resplen, success, ret; + int reqlen, resplen, success; void *vresponse; PUT_32BIT(request, 1); request[4] = SSH2_AGENTC_REMOVE_ALL_IDENTITIES; reqlen = 5; - ret = agent_query(request, reqlen, &vresponse, &resplen, NULL, NULL); - assert(ret == 1); + agent_query_synchronous(request, reqlen, &vresponse, &resplen); response = vresponse; success = (resplen >= 4 && response[4] == SSH_AGENT_SUCCESS); sfree(response); @@ -1778,8 +1771,7 @@ int pageant_delete_all_keys(char **retstr) PUT_32BIT(request, 1); request[4] = SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES; reqlen = 5; - ret = agent_query(request, reqlen, &vresponse, &resplen, NULL, NULL); - assert(ret == 1); + agent_query_synchronous(request, reqlen, &vresponse, &resplen); response = vresponse; success = (resplen >= 4 && response[4] == SSH_AGENT_SUCCESS); sfree(response); diff --git a/putty.h b/putty.h index 943ccbf9..416f8903 100644 --- a/putty.h +++ b/putty.h @@ -1200,17 +1200,32 @@ void crypto_wrapup(); /* * 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 + * agent_query returns NULL for here's-a-response, and non-NULL 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. + * + * When the return from agent_query is not NULL, it identifies the + * in-progress query in case it needs to be cancelled. If + * agent_cancel_query is called, then the pending query is destroyed + * and the callback will not be called. (E.g. if you're going to throw + * away the thing you were using as callback_ctx.) + * + * Passing a null pointer as callback forces agent_query to behave + * synchronously, i.e. it will block if necessary, and guarantee to + * return NULL. The wrapper function agent_query_synchronous() makes + * this easier. */ -int agent_query(void *in, int inlen, void **out, int *outlen, - void (*callback)(void *, void *, int), void *callback_ctx); +typedef struct agent_pending_query agent_pending_query; +agent_pending_query *agent_query( + void *in, int inlen, void **out, int *outlen, + void (*callback)(void *, void *, int), void *callback_ctx); +void agent_cancel_query(agent_pending_query *); +void agent_query_synchronous(void *in, int inlen, void **out, int *outlen); int agent_exists(void); /* diff --git a/ssh.c b/ssh.c index 7e74fb44..2212c2b7 100644 --- a/ssh.c +++ b/ssh.c @@ -577,6 +577,7 @@ struct ssh_channel { unsigned char msglen[4]; unsigned lensofar, totallen; int outstanding_requests; + agent_pending_query *pending; } a; struct ssh_x11_channel { struct X11Connection *xconn; @@ -985,6 +986,13 @@ struct ssh_tag { * with a newly cross-certified host key. */ int cross_certifying; + + /* + * Any asynchronous query to our SSH agent that we might have in + * flight from the main authentication loop. (Queries from + * agent-forwarding channels live in their channel structure.) + */ + agent_pending_query *auth_agent_query; }; static const char *ssh_pkt_type(Ssh ssh, int type) @@ -3811,6 +3819,8 @@ static void ssh_agent_callback(void *sshv, void *reply, int replylen) { Ssh ssh = (Ssh) sshv; + ssh->auth_agent_query = NULL; + ssh->agent_response = reply; ssh->agent_response_len = replylen; @@ -3843,6 +3853,7 @@ static void ssh_agentf_callback(void *cv, void *reply, int replylen) struct ssh_channel *c = (struct ssh_channel *)cv; const void *sentreply = reply; + c->u.a.pending = NULL; c->u.a.outstanding_requests--; if (!sentreply) { /* Fake SSH_AGENT_FAILURE. */ @@ -4362,8 +4373,9 @@ static int do_ssh1_login(Ssh ssh, const unsigned char *in, int inlen, /* Request the keys held by the agent. */ PUT_32BIT(s->request, 1); s->request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES; - if (!agent_query(s->request, 5, &r, &s->responselen, - ssh_agent_callback, ssh)) { + ssh->auth_agent_query = agent_query( + s->request, 5, &r, &s->responselen, ssh_agent_callback, ssh); + if (ssh->auth_agent_query) { do { crReturn(0); if (pktin) { @@ -4468,8 +4480,10 @@ static int do_ssh1_login(Ssh ssh, const unsigned char *in, int inlen, memcpy(q, s->session_id, 16); q += 16; PUT_32BIT(q, 1); /* response format */ - if (!agent_query(agentreq, len + 4, &vret, &retlen, - ssh_agent_callback, ssh)) { + ssh->auth_agent_query = agent_query( + agentreq, len + 4, &vret, &retlen, + ssh_agent_callback, ssh); + if (ssh->auth_agent_query) { sfree(agentreq); do { crReturn(0); @@ -5541,6 +5555,7 @@ static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin) c->type = CHAN_AGENT; /* identify channel type */ c->u.a.lensofar = 0; c->u.a.message = NULL; + c->u.a.pending = NULL; c->u.a.outstanding_requests = 0; send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION, PKT_INT, c->remoteid, PKT_INT, c->localid, @@ -5707,9 +5722,11 @@ static int ssh_agent_channel_data(struct ssh_channel *c, char *data, void *reply; int replylen; c->u.a.outstanding_requests++; - if (agent_query(c->u.a.message, c->u.a.totallen, &reply, &replylen, - ssh_agentf_callback, c)) - ssh_agentf_callback(c, reply, replylen); + c->u.a.pending = agent_query( + c->u.a.message, c->u.a.totallen, &reply, &replylen, + ssh_agentf_callback, c); + if (!c->u.a.pending) + ssh_agentf_callback(c, reply, replylen); sfree(c->u.a.message); c->u.a.message = NULL; c->u.a.lensofar = 0; @@ -8141,6 +8158,8 @@ static void ssh_channel_close_local(struct ssh_channel *c, char const *reason) msg = "Forwarded X11 connection terminated"; break; case CHAN_AGENT: + if (c->u.a.pending) + agent_cancel_query(c->u.a.pending); sfree(c->u.a.message); break; case CHAN_SOCKDATA: @@ -8788,6 +8807,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin) c->type = CHAN_AGENT; /* identify channel type */ c->u.a.lensofar = 0; c->u.a.message = NULL; + c->u.a.pending = NULL; c->u.a.outstanding_requests = 0; } } else { @@ -9291,8 +9311,10 @@ static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen, /* Request the keys held by the agent. */ PUT_32BIT(s->agent_request, 1); s->agent_request[4] = SSH2_AGENTC_REQUEST_IDENTITIES; - if (!agent_query(s->agent_request, 5, &r, &s->agent_responselen, - ssh_agent_callback, ssh)) { + ssh->auth_agent_query = agent_query( + s->agent_request, 5, &r, &s->agent_responselen, + ssh_agent_callback, ssh); + if (ssh->auth_agent_query) { do { crReturnV; if (pktin) { @@ -9730,9 +9752,10 @@ static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen, s->q += s->pktout->length - 5; /* And finally the (zero) flags word. */ PUT_32BIT(s->q, 0); - if (!agent_query(s->agentreq, s->len + 4, - &vret, &s->retlen, - ssh_agent_callback, ssh)) { + ssh->auth_agent_query = agent_query( + s->agentreq, s->len + 4, &vret, &s->retlen, + ssh_agent_callback, ssh); + if (ssh->auth_agent_query) { do { crReturnV; if (pktin) { @@ -11168,6 +11191,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, CONF_ssh_rekey_data)); ssh->kex_in_progress = FALSE; + ssh->auth_agent_query = NULL; + #ifndef NO_GSSAPI ssh->gsslibs = NULL; #endif @@ -11283,6 +11308,10 @@ static void ssh_free(void *handle) bufchain_clear(&ssh->queued_incoming_data); sfree(ssh->username); conf_free(ssh->conf); + + if (ssh->auth_agent_query) + agent_cancel_query(ssh->auth_agent_query); + #ifndef NO_GSSAPI if (ssh->gsslibs) ssh_gss_cleanup(ssh->gsslibs); diff --git a/unix/uxagentc.c b/unix/uxagentc.c index 326ee66b..7732a9a1 100644 --- a/unix/uxagentc.c +++ b/unix/uxagentc.c @@ -23,8 +23,8 @@ int agent_exists(void) return FALSE; } -static tree234 *agent_connections; -struct agent_connection { +static tree234 *agent_pending_queries; +struct agent_pending_query { int fd; char *retbuf; char sizebuf[4]; @@ -34,8 +34,8 @@ struct agent_connection { }; static int agent_conncmp(void *av, void *bv) { - struct agent_connection *a = (struct agent_connection *) av; - struct agent_connection *b = (struct agent_connection *) bv; + agent_pending_query *a = (agent_pending_query *) av; + agent_pending_query *b = (agent_pending_query *) bv; if (a->fd < b->fd) return -1; if (a->fd > b->fd) @@ -45,7 +45,7 @@ static int agent_conncmp(void *av, void *bv) static int agent_connfind(void *av, void *bv) { int afd = *(int *) av; - struct agent_connection *b = (struct agent_connection *) bv; + agent_pending_query *b = (agent_pending_query *) bv; if (afd < b->fd) return -1; if (afd > b->fd) @@ -59,7 +59,7 @@ static int agent_connfind(void *av, void *bv) * (conn->retbuf non-NULL and filled with something useful) or has * failed totally (conn->retbuf is NULL). */ -static int agent_try_read(struct agent_connection *conn) +static int agent_try_read(agent_pending_query *conn) { int ret; @@ -90,13 +90,21 @@ static int agent_try_read(struct agent_connection *conn) return 1; } +void agent_cancel_query(agent_pending_query *conn) +{ + uxsel_del(conn->fd); + close(conn->fd); + del234(agent_pending_queries, conn); + sfree(conn); +} + static int agent_select_result(int fd, int event) { - struct agent_connection *conn; + agent_pending_query *conn; assert(event == 1); /* not selecting for anything but R */ - conn = find234(agent_connections, &fd, agent_connfind); + conn = find234(agent_pending_queries, &fd, agent_connfind); if (!conn) { uxsel_del(fd); return 1; @@ -111,21 +119,19 @@ static int agent_select_result(int fd, int event) * 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); + agent_cancel_query(conn); return 0; } -int agent_query(void *in, int inlen, void **out, int *outlen, - void (*callback)(void *, void *, int), void *callback_ctx) +agent_pending_query *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; - struct agent_connection *conn; + agent_pending_query *conn; name = getenv("SSH_AUTH_SOCK"); if (!name) @@ -155,7 +161,7 @@ int agent_query(void *in, int inlen, void **out, int *outlen, done += ret; } - conn = snew(struct agent_connection); + conn = snew(agent_pending_query); conn->fd = sock; conn->retbuf = conn->sizebuf; conn->retsize = 4; @@ -179,7 +185,7 @@ int agent_query(void *in, int inlen, void **out, int *outlen, *out = conn->retbuf; *outlen = conn->retlen; sfree(conn); - return 1; + return NULL; } /* @@ -188,15 +194,15 @@ int agent_query(void *in, int inlen, void **out, int *outlen, * response hasn't been received yet, and call the callback when * select_result comes back to us. */ - if (!agent_connections) - agent_connections = newtree234(agent_conncmp); - add234(agent_connections, conn); + if (!agent_pending_queries) + agent_pending_queries = newtree234(agent_conncmp); + add234(agent_pending_queries, conn); uxsel_set(sock, 1, agent_select_result); - return 0; + return conn; failure: *out = NULL; *outlen = 0; - return 1; + return NULL; } diff --git a/windows/winpgntc.c b/windows/winpgntc.c index 6e085e42..af2abb6f 100644 --- a/windows/winpgntc.c +++ b/windows/winpgntc.c @@ -24,8 +24,14 @@ int agent_exists(void) return TRUE; } -int agent_query(void *in, int inlen, void **out, int *outlen, - void (*callback)(void *, void *, int), void *callback_ctx) +void agent_cancel_query(agent_pending_query *q) +{ + assert(0 && "Windows agent queries are never asynchronous!"); +} + +agent_pending_query *agent_query( + void *in, int inlen, void **out, int *outlen, + void (*callback)(void *, void *, int), void *callback_ctx) { HWND hwnd; char *mapname; @@ -42,7 +48,7 @@ int agent_query(void *in, int inlen, void **out, int *outlen, hwnd = FindWindow("Pageant", "Pageant"); if (!hwnd) - return 1; /* *out == NULL, so failure */ + return NULL; /* *out == NULL, so failure */ mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId()); psa = NULL; @@ -83,7 +89,7 @@ int agent_query(void *in, int inlen, void **out, int *outlen, 0, AGENT_MAX_MSGLEN, mapname); if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) { sfree(mapname); - return 1; /* *out == NULL, so failure */ + return NULL; /* *out == NULL, so failure */ } p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0); memcpy(p, in, inlen); @@ -111,5 +117,5 @@ int agent_query(void *in, int inlen, void **out, int *outlen, sfree(mapname); if (psd) LocalFree(psd); - return 1; + return NULL; }