]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
Make asynchronous agent_query() requests cancellable.
authorSimon Tatham <anakin@pobox.com>
Sun, 29 Jan 2017 20:24:15 +0000 (20:24 +0000)
committerSimon Tatham <anakin@pobox.com>
Sun, 29 Jan 2017 20:25:04 +0000 (20:25 +0000)
Now, instead of returning a boolean indicating whether the query has
completed or is still pending, agent_query() returns NULL to indicate
that the query _has_ completed, and if it hasn't, it returns a pointer
to a context structure representing the pending query, so that the
latter can be used to cancel the query if (for example) you later
decide you need to free the thing its callback was using as a context.

This should fix a potential race-condition segfault if you overload an
agent forwarding channel and then close it abruptly. (Which nobody
will be doing for sensible purposes, of course! But I ran across this
while stress-testing other aspects of agent forwarding.)

Recipe
aqsync.c [new file with mode: 0644]
pageant.c
putty.h
ssh.c
unix/uxagentc.c
windows/winpgntc.c

diff --git a/Recipe b/Recipe
index 3b2db901b650fe84fa2043cdfacbbb9cd4fa698e..48cad133181494faed86a58395d358a51ae83d7d 100644 (file)
--- 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 (file)
index 0000000..e16eadd
--- /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 <assert.h>
+
+#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);
+}
+
index c008f008030b8619092f7eac3e74acf36decfe45..68bfab5309f134f3fe3a60c09d2944b3f20a3e22 100644 (file)
--- 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 943ccbf98dd18f53509ad92f615f11d3db679aba..416f8903eb1aa520157f673e1fbfd4f577bb95cd 100644 (file)
--- 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 7e74fb44915ef161f5abf8ede3bae514cdb0dd10..2212c2b77764cc55505363352fa054ba239ab846 100644 (file)
--- 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);
index 326ee66b832439e35cb40a471339bc989074669c..7732a9a1744842e69fd05d23dcf10d7fdee64260 100644 (file)
@@ -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;
 }
index 6e085e424471e62c398fad515a09f6879a17b0f2..af2abb6fb5a667025e33ebd003bfa1de4b68a065 100644 (file)
@@ -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;
 }