Explorar o código

Merge branch 'thirdparty_dev' into dev

Conflicts:
	source/putty/WINDOWS/winnet.c
	source/putty/WINDOWS/winpgntc.c
	source/putty/misc.c
	source/putty/putty.h
	source/putty/ssh.c

Source commit: fd33885cae360a65ddc841eabeda3718b8a67e74
Martin Prikryl %!s(int64=8) %!d(string=hai) anos
pai
achega
92de9fd3e6

+ 131 - 17
source/putty/misc.c

@@ -394,22 +394,16 @@ int toint(unsigned u)
  *    directive we don't know about, we should panic and die rather
  *    than run any risk.
  */
-char *dupprintf(const char *fmt, ...)
-{
-    char *ret;
-    va_list ap;
-    va_start(ap, fmt);
-    ret = dupvprintf(fmt, ap);
-    va_end(ap);
-    return ret;
-}
-char *dupvprintf(const char *fmt, va_list ap)
+static char *dupvprintf_inner(char *buf, int oldlen, int oldsize,
+                              const char *fmt, va_list ap)
 {
-    char *buf;
     int len, size;
 
-    buf = snewn(512, char);
-    size = 512;
+    size = oldsize - oldlen;
+    if (size == 0) {
+        size = 512;
+        buf = sresize(buf, oldlen + size, char);
+    }
 
     while (1) {
 #if defined _WINDOWS && _MSC_VER < 1900 /* 1900 == VS2015 has real snprintf */
@@ -420,7 +414,7 @@ char *dupvprintf(const char *fmt, va_list ap)
 	 * XXX some environments may have this as __va_copy() */
 	va_list aq;
 	va_copy(aq, ap);
-	len = vsnprintf(buf, size, fmt, aq);
+	len = vsnprintf(buf + oldlen, size, fmt, aq);
 	va_end(aq);
 #else
 	/* Ugh. No va_copy macro, so do something nasty.
@@ -431,7 +425,7 @@ char *dupvprintf(const char *fmt, va_list ap)
 	 * (indeed, it has been observed to).
 	 * XXX the autoconf manual suggests that using memcpy() will give
 	 *     "maximum portability". */
-	len = vsnprintf(buf, size, fmt, ap);
+	len = vsnprintf(buf + oldlen, size, fmt, ap);
 #endif
 	if (len >= 0 && len < size) {
 	    /* This is the C99-specified criterion for snprintf to have
@@ -446,10 +440,65 @@ char *dupvprintf(const char *fmt, va_list ap)
 	     * buffer wasn't big enough, so we enlarge it a bit and hope. */
 	    size += 512;
 	}
-	buf = sresize(buf, size, char);
+	buf = sresize(buf, oldlen + size, char);
     }
 }
 
+char *dupvprintf(const char *fmt, va_list ap)
+{
+    return dupvprintf_inner(NULL, 0, 0, fmt, ap);
+}
+char *dupprintf(const char *fmt, ...)
+{
+    char *ret;
+    va_list ap;
+    va_start(ap, fmt);
+    ret = dupvprintf(fmt, ap);
+    va_end(ap);
+    return ret;
+}
+
+struct strbuf {
+    char *s;
+    int len, size;
+};
+strbuf *strbuf_new(void)
+{
+    strbuf *buf = snew(strbuf);
+    buf->len = 0;
+    buf->size = 512;
+    buf->s = snewn(buf->size, char);
+    *buf->s = '\0';
+    return buf;
+}
+void strbuf_free(strbuf *buf)
+{
+    sfree(buf->s);
+    sfree(buf);
+}
+char *strbuf_str(strbuf *buf)
+{
+    return buf->s;
+}
+char *strbuf_to_str(strbuf *buf)
+{
+    char *ret = buf->s;
+    sfree(buf);
+    return ret;
+}
+void strbuf_catfv(strbuf *buf, const char *fmt, va_list ap)
+{
+    buf->s = dupvprintf_inner(buf->s, buf->len, buf->size, fmt, ap);
+    buf->len += strlen(buf->s + buf->len);
+}
+void strbuf_catf(strbuf *buf, const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    strbuf_catfv(buf, fmt, ap);
+    va_end(ap);
+}
+
 /*
  * Read an entire line of text from a file. Return a buffer
  * malloced to be as big as necessary (caller must free).
@@ -1072,7 +1121,7 @@ void *get_ssh_string(int *datalen, const void **data, int *stringlen)
     if (*datalen < 4)
         return NULL;
     len = GET_32BIT_MSB_FIRST((const unsigned char *)*data);
-    if (*datalen < len+4)
+    if (*datalen - 4 < len)
         return NULL;
     ret = (void *)((const char *)*data + 4);
     *datalen -= len + 4;
@@ -1102,6 +1151,71 @@ int strendswith(const char *s, const char *t)
     return slen >= tlen && !strcmp(s + (slen - tlen), t);
 }
 
+char *buildinfo(const char *newline)
+{
+    strbuf *buf = strbuf_new();
+    extern const char commitid[];      /* in commitid.c */
+
+    strbuf_catf(buf, "Build platform: %d-bit %s",
+                (int)(CHAR_BIT * sizeof(void *)),
+                BUILDINFO_PLATFORM);
+
+#ifdef __clang_version__
+    strbuf_catf(buf, "%sCompiler: clang %s", newline, __clang_version__);
+#elif defined __GNUC__ && defined __VERSION__
+    strbuf_catf(buf, "%sCompiler: gcc %s", newline, __VERSION__);
+#elif defined _MSC_VER
+    strbuf_catf(buf, "%sCompiler: Visual Studio", newline);
+#if _MSC_VER == 1900
+    strbuf_catf(buf, " 2015 / MSVC++ 14.0");
+#elif _MSC_VER == 1800
+    strbuf_catf(buf, " 2013 / MSVC++ 12.0");
+#elif _MSC_VER == 1700
+    strbuf_catf(buf, " 2012 / MSVC++ 11.0");
+#elif _MSC_VER == 1600
+    strbuf_catf(buf, " 2010 / MSVC++ 10.0");
+#elif  _MSC_VER == 1500
+    strbuf_catf(buf, " 2008 / MSVC++ 9.0");
+#elif  _MSC_VER == 1400
+    strbuf_catf(buf, " 2005 / MSVC++ 8.0");
+#elif  _MSC_VER == 1310
+    strbuf_catf(buf, " 2003 / MSVC++ 7.1");
+#else
+    strbuf_catf(buf, ", unrecognised version");
+#endif
+    strbuf_catf(buf, " (_MSC_VER=%d)", (int)_MSC_VER);
+#endif
+
+#ifdef NO_SECURITY
+    strbuf_catf(buf, "%sBuild option: NO_SECURITY", newline);
+#endif
+#ifdef NO_SECUREZEROMEMORY
+    strbuf_catf(buf, "%sBuild option: NO_SECUREZEROMEMORY", newline);
+#endif
+#ifdef NO_IPV6
+    strbuf_catf(buf, "%sBuild option: NO_IPV6", newline);
+#endif
+#ifdef NO_GSSAPI
+    strbuf_catf(buf, "%sBuild option: NO_GSSAPI", newline);
+#endif
+#ifdef STATIC_GSSAPI
+    strbuf_catf(buf, "%sBuild option: STATIC_GSSAPI", newline);
+#endif
+#ifdef UNPROTECT
+    strbuf_catf(buf, "%sBuild option: UNPROTECT", newline);
+#endif
+#ifdef FUZZING
+    strbuf_catf(buf, "%sBuild option: FUZZING", newline);
+#endif
+#ifdef DEBUG
+    strbuf_catf(buf, "%sBuild option: DEBUG", newline);
+#endif
+
+    strbuf_catf(buf, "%sSource commit: %s", newline, commitid);
+
+    return strbuf_to_str(buf);
+}
+
 #ifdef MPEXT
 
 #include "version.h"

+ 9 - 0
source/putty/misc.h

@@ -38,6 +38,13 @@ char *dupprintf(const char *fmt, ...)
     ;
 char *dupvprintf(const char *fmt, va_list ap);
 void burnstr(char *string);
+typedef struct strbuf strbuf;
+strbuf *strbuf_new(void);
+void strbuf_free(strbuf *buf);
+char *strbuf_str(strbuf *buf);         /* does not free buf */
+char *strbuf_to_str(strbuf *buf); /* does free buf, but you must free result */
+void strbuf_catf(strbuf *buf, const char *fmt, ...);
+void strbuf_catfv(strbuf *buf, const char *fmt, va_list ap);
 
 /* String-to-Unicode converters that auto-allocate the destination and
  * work around the rather deficient interface of mb_to_wc.
@@ -111,6 +118,8 @@ int get_ssh_uint32(int *datalen, const void **data, unsigned *ret);
  * form, check if it equals an ordinary C zero-terminated string. */
 int match_ssh_id(int stringlen, const void *string, const char *id);
 
+char *buildinfo(const char *newline);
+
 /*
  * Debugging functions.
  *

+ 21 - 5
source/putty/putty.h

@@ -770,6 +770,7 @@ void cleanup_exit(int);
     X(INT, NONE, no_remote_resize) /* disable remote resizing */ \
     X(INT, NONE, no_alt_screen) /* disable alternate screen */ \
     X(INT, NONE, no_remote_wintitle) /* disable remote retitling */ \
+    X(INT, NONE, no_remote_clearscroll) /* disable ESC[3J */ \
     X(INT, NONE, no_dbackspace) /* disable destructive backspace */ \
     X(INT, NONE, no_remote_charset) /* disable remote charset config */ \
     X(INT, NONE, remote_qtitle_action) /* remote win title query action */ \
@@ -1205,17 +1206,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);
 
 /*

+ 193 - 74
source/putty/ssh.c

@@ -10,6 +10,7 @@
 #include <signal.h>
 
 #include "putty.h"
+#include "pageant.h" /* for AGENT_MAX_MSGLEN */
 #include "tree234.h"
 #include "storage.h"
 #include "ssh.h"
@@ -573,10 +574,8 @@ struct ssh_channel {
     } v;
     union {
 	struct ssh_agent_channel {
-	    unsigned char *message;
-	    unsigned char msglen[4];
-	    unsigned lensofar, totallen;
-            int outstanding_requests;
+            bufchain inbuffer;
+            agent_pending_query *pending;
 	} a;
 	struct ssh_x11_channel {
 	    struct X11Connection *xconn;
@@ -985,6 +984,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)
@@ -3789,6 +3795,8 @@ static void ssh_throttle_conn(Ssh ssh, int adjust)
     }
 }
 
+static void ssh_agentf_try_forward(struct ssh_channel *c);
+
 /*
  * Throttle or unthrottle _all_ local data streams (for when sends
  * on the SSH connection itself back up).
@@ -3815,7 +3823,12 @@ static void ssh_throttle_all(Ssh ssh, int enable, int bufsize)
 	    x11_override_throttle(c->u.x11.xconn, enable);
 	    break;
 	  case CHAN_AGENT:
-	    /* Agent channels require no buffer management. */
+	    /* Agent forwarding channels are buffer-managed by
+             * checking ssh->throttled_all in ssh_agentf_try_forward.
+             * So at the moment we _un_throttle again, we must make an
+             * attempt to do something. */
+            if (!enable)
+                ssh_agentf_try_forward(c);
 	    break;
 	  case CHAN_SOCKDATA:
 	    pfd_override_throttle(c->u.pfd.pf, enable);
@@ -3828,6 +3841,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;
 
@@ -3855,28 +3870,139 @@ static void ssh_dialog_callback(void *sshv, int ret)
     ssh_process_queued_incoming_data(ssh);
 }
 
-static void ssh_agentf_callback(void *cv, void *reply, int replylen)
+static void ssh_agentf_got_response(struct ssh_channel *c,
+                                    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. */
-	sentreply = "\0\0\0\1\5";
+    assert(!(c->closes & CLOSES_SENT_EOF));
+
+    if (!reply) {
+	/* The real agent didn't send any kind of reply at all for
+         * some reason, so fake an SSH_AGENT_FAILURE. */
+	reply = "\0\0\0\1\5";
 	replylen = 5;
     }
-    ssh_send_channel_data(c, sentreply, replylen);
-    if (reply)
-	sfree(reply);
+
+    ssh_send_channel_data(c, reply, replylen);
+}
+
+static void ssh_agentf_callback(void *cv, void *reply, int replylen);
+
+static void ssh_agentf_try_forward(struct ssh_channel *c)
+{
+    unsigned datalen, lengthfield, messagelen;
+    unsigned char *message;
+    unsigned char msglen[4];
+    void *reply;
+    int replylen;
+
+    /*
+     * Don't try to parallelise agent requests. Wait for each one to
+     * return before attempting the next.
+     */
+    if (c->u.a.pending)
+        return;
+
+    /*
+     * If the outgoing side of the channel connection is currently
+     * throttled (for any reason, either that channel's window size or
+     * the entire SSH connection being throttled), don't submit any
+     * new forwarded requests to the real agent. This causes the input
+     * side of the agent forwarding not to be emptied, exerting the
+     * required back-pressure on the remote client, and encouraging it
+     * to read our responses before sending too many more requests.
+     */
+    if (c->ssh->throttled_all ||
+        (c->ssh->version == 2 && c->v.v2.remwindow == 0))
+        return;
+
+    if (c->closes & CLOSES_SENT_EOF) {
+        /*
+         * If we've already sent outgoing EOF, there's nothing we can
+         * do with incoming data except consume it and throw it away.
+         */
+        bufchain_clear(&c->u.a.inbuffer);
+        return;
+    }
+
+    while (1) {
+        /*
+         * Try to extract a complete message from the input buffer.
+         */
+        datalen = bufchain_size(&c->u.a.inbuffer);
+        if (datalen < 4)
+            break;         /* not even a length field available yet */
+
+        bufchain_fetch(&c->u.a.inbuffer, msglen, 4);
+        lengthfield = GET_32BIT(msglen);
+
+        if (lengthfield > AGENT_MAX_MSGLEN) {
+            /*
+             * If the remote has sent a message that's just _too_
+             * long, we should reject it in advance of seeing the rest
+             * of the incoming message, and also close the connection
+             * for good measure (which avoids us having to faff about
+             * with carefully ignoring just the right number of bytes
+             * from the overlong message).
+             */
+            ssh_agentf_got_response(c, NULL, 0);
+            sshfwd_write_eof(c);
+            return;
+        }
+
+        if (lengthfield > datalen - 4)
+            break;          /* a whole message is not yet available */
+
+        messagelen = lengthfield + 4;
+
+        message = snewn(messagelen, unsigned char);
+        bufchain_fetch(&c->u.a.inbuffer, message, messagelen);
+        bufchain_consume(&c->u.a.inbuffer, messagelen);
+        c->u.a.pending = agent_query(
+            message, messagelen, &reply, &replylen, ssh_agentf_callback, c);
+        sfree(message);
+
+        if (c->u.a.pending)
+            return;   /* agent_query promised to reply in due course */
+
+        /*
+         * If the agent gave us an answer immediately, pass it
+         * straight on and go round this loop again.
+         */
+        ssh_agentf_got_response(c, reply, replylen);
+    }
+
     /*
-     * If we've already seen an incoming EOF but haven't sent an
-     * outgoing one, this may be the moment to send it.
+     * If we get here (i.e. we left the above while loop via 'break'
+     * rather than 'return'), that means we've determined that the
+     * input buffer for the agent forwarding connection doesn't
+     * contain a complete request.
+     *
+     * So if there's potentially more data to come, we can return now,
+     * and wait for the remote client to send it. But if the remote
+     * has sent EOF, it would be a mistake to do that, because we'd be
+     * waiting a long time. So this is the moment to check for EOF,
+     * and respond appropriately.
      */
-    if (c->u.a.outstanding_requests == 0 && (c->closes & CLOSES_RCVD_EOF))
+    if (c->closes & CLOSES_RCVD_EOF)
         sshfwd_write_eof(c);
 }
 
+static void ssh_agentf_callback(void *cv, void *reply, int replylen)
+{
+    struct ssh_channel *c = (struct ssh_channel *)cv;
+
+    ssh_agentf_got_response(c, reply, replylen);
+    sfree(reply);
+
+    /*
+     * Now try to extract and send further messages from the channel's
+     * input-side buffer.
+     */
+    ssh_agentf_try_forward(c);
+}
+
 /*
  * Client-initiated disconnection. Send a DISCONNECT if `wire_reason'
  * non-NULL, otherwise just close the connection. `client_reason' == NULL
@@ -4379,8 +4505,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) {
@@ -4485,8 +4612,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);
@@ -5556,9 +5685,8 @@ static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin)
 	c->remoteid = remoteid;
 	c->halfopen = FALSE;
 	c->type = CHAN_AGENT;	/* identify channel type */
-	c->u.a.lensofar = 0;
-	c->u.a.message = NULL;
-	c->u.a.outstanding_requests = 0;
+	c->u.a.pending = NULL;
+        bufchain_init(&c->u.a.inbuffer);
 	send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
 		    PKT_INT, c->remoteid, PKT_INT, c->localid,
 		    PKT_END);
@@ -5699,40 +5827,18 @@ static void ssh1_msg_channel_close(Ssh ssh, struct Packet *pktin)
 static int ssh_agent_channel_data(struct ssh_channel *c, char *data,
 				  int length)
 {
-    while (length > 0) {
-	if (c->u.a.lensofar < 4) {
-	    unsigned int l = min(4 - c->u.a.lensofar, (unsigned)length);
-	    memcpy(c->u.a.msglen + c->u.a.lensofar, data, l);
-	    data += l;
-	    length -= l;
-	    c->u.a.lensofar += l;
-	}
-	if (c->u.a.lensofar == 4) {
-	    c->u.a.totallen = 4 + GET_32BIT(c->u.a.msglen);
-	    c->u.a.message = snewn(c->u.a.totallen, unsigned char);
-	    memcpy(c->u.a.message, c->u.a.msglen, 4);
-	}
-	if (c->u.a.lensofar >= 4 && length > 0) {
-	    unsigned int l = min(c->u.a.totallen - c->u.a.lensofar,
-				 (unsigned)length);
-	    memcpy(c->u.a.message + c->u.a.lensofar, data, l);
-	    data += l;
-	    length -= l;
-	    c->u.a.lensofar += l;
-	}
-	if (c->u.a.lensofar == c->u.a.totallen) {
-	    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);
-	    sfree(c->u.a.message);
-            c->u.a.message = NULL;
-	    c->u.a.lensofar = 0;
-	}
-    }
-    return 0;   /* agent channels never back up */
+    bufchain_add(&c->u.a.inbuffer, data, length);
+    ssh_agentf_try_forward(c);
+
+    /*
+     * We exert back-pressure on an agent forwarding client if and
+     * only if we're waiting for the response to an asynchronous agent
+     * request. This prevents the client running out of window while
+     * receiving the _first_ message, but means that if any message
+     * takes time to process, the client will be discouraged from
+     * sending an endless stream of further ones after it.
+     */
+    return (c->u.a.pending ? bufchain_size(&c->u.a.inbuffer) : 0);
 }
 
 static int ssh_channel_data(struct ssh_channel *c, int is_stderr,
@@ -7348,6 +7454,7 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen,
         s->fingerprint = ssh2_fingerprint(ssh->hostkey, s->hkey);
         logevent("Storing additional host key for this host:");
         logevent(s->fingerprint);
+        sfree(s->fingerprint);
         store_host_key(ssh->savedhost, ssh->savedport,
                        ssh->hostkey->keytype, s->keystr);
         ssh->cross_certifying = FALSE;
@@ -7740,8 +7847,9 @@ static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c)
 	    x11_unthrottle(c->u.x11.xconn);
 	    break;
 	  case CHAN_AGENT:
-	    /* agent sockets are request/response and need no
-	     * buffer management */
+            /* Now that we've successfully sent all the outgoing
+             * replies we had, try to process more incoming data. */
+            ssh_agentf_try_forward(c);
 	    break;
 	  case CHAN_SOCKDATA:
 	    pfd_unthrottle(c->u.pfd.pf);
@@ -8165,7 +8273,10 @@ static void ssh_channel_close_local(struct ssh_channel *c, char const *reason)
         msg = "Forwarded X11 connection terminated";
         break;
       case CHAN_AGENT:
-        sfree(c->u.a.message);
+        if (c->u.a.pending)
+            agent_cancel_query(c->u.a.pending);
+        bufchain_clear(&c->u.a.inbuffer);
+	msg = "Agent-forwarding connection closed";
         break;
       case CHAN_SOCKDATA:
         assert(c->u.pfd.pf != NULL);
@@ -8253,10 +8364,10 @@ static void ssh_channel_got_eof(struct ssh_channel *c)
 	assert(c->u.x11.xconn != NULL);
 	x11_send_eof(c->u.x11.xconn);
     } else if (c->type == CHAN_AGENT) {
-        if (c->u.a.outstanding_requests == 0) {
-            /* Manufacture an outgoing EOF in response to the incoming one. */
-            sshfwd_write_eof(c);
-        }
+        /* Just call try_forward, which will respond to the EOF now if
+         * appropriate, or wait until the queue of outstanding
+         * requests is dealt with if not */
+        ssh_agentf_try_forward(c);
     } else if (c->type == CHAN_SOCKDATA) {
 	assert(c->u.pfd.pf != NULL);
 	pfd_send_eof(c->u.pfd.pf);
@@ -8810,9 +8921,8 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
 	    error = "Agent forwarding is not enabled";
 	else {
 	    c->type = CHAN_AGENT;	/* identify channel type */
-	    c->u.a.lensofar = 0;
-            c->u.a.message = NULL;
-            c->u.a.outstanding_requests = 0;
+            bufchain_init(&c->u.a.inbuffer);
+            c->u.a.pending = NULL;
 	}
     } else {
 	error = "Unsupported channel type requested";
@@ -9324,8 +9434,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) {
@@ -9769,9 +9881,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) {
@@ -11272,6 +11385,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
@@ -11388,6 +11503,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);

+ 1 - 0
source/putty/sshbn.c

@@ -1461,6 +1461,7 @@ Bignum bignum_from_decimal(const char *decimal)
 
         tmp = bigmul(result, Ten);
         tmp2 = bignum_from_long(*decimal - '0');
+        freebn(result);
         result = bigadd(tmp, tmp2);
         freebn(tmp);
         freebn(tmp2);

+ 3 - 3
source/putty/sshecc.c

@@ -2113,10 +2113,10 @@ static void *ed25519_openssh_createkey(const struct ssh_signkey *self,
     }
 
     getstring((const char**)blob, len, &q, &qlen);
-    if (!q)
-        return NULL;
-    if (qlen != 64)
+    if (!q || qlen != 64) {
+        ecdsa_freekey(ec);
         return NULL;
+    }
 
     ec->privateKey = bignum_from_bytes_le((const unsigned char *)q, 32);
 

+ 3 - 0
source/putty/sshpubk.c

@@ -309,6 +309,8 @@ int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen,
             *commentptr = commentp ? dupstr(commentp) : NULL;
         *blob = rsa_public_blob(&key, bloblen);
         freersakey(&key);
+        sfree(line);
+        fclose(fp);
         return 1;
 
       not_public_either:
@@ -1089,6 +1091,7 @@ unsigned char *openssh_loadpub(FILE *fp, char **algorithm,
         *commentptr = comment;
     else
         sfree(comment);
+    sfree(line);
     return pubblob;
 
   error:

+ 1 - 1
source/putty/sshrand.c

@@ -240,7 +240,7 @@ void random_add_noise(void *noise, int length)
 	length -= HASHINPUT - pool.incomingpos;
 	SHATransform((word32 *) pool.incoming, (word32 *) pool.incomingb);
 	for (i = 0; i < HASHSIZE; i++) {
-	    pool.pool[pool.poolpos++] ^= pool.incomingb[i];
+	    pool.pool[pool.poolpos++] ^= pool.incoming[i];
 	    if (pool.poolpos >= POOLSIZE)
 		pool.poolpos = 0;
 	}

+ 4 - 3
source/putty/version.h

@@ -1,5 +1,6 @@
 /* Generated by automated build script */
 #define SNAPSHOT
-#define TEXTVER "Development snapshot 2016-07-19.9398d23"
-#define SSHVER "PuTTY-Snapshot-2016-07-19.9398d23"
-#define BINARY_VERSION 0,67,1141,0
+#define TEXTVER "Development snapshot 2017-02-02.f6c1c88"
+#define SSHVER "PuTTY-Snapshot-2017-02-02.f6c1c88"
+#define BINARY_VERSION 0,67,1339,0
+#define SOURCE_COMMIT "f6c1c8819b5d90a97124b62ee07b0e06d6bbb6c3"

+ 44 - 8
source/putty/windows/winnet.c

@@ -723,6 +723,35 @@ void sk_getaddr(SockAddr addr, char *buf, int buflen)
     }
 }
 
+/*
+ * This constructs a SockAddr that points at one specific sub-address
+ * of a parent SockAddr. The returned SockAddr does not own all its
+ * own memory: it points into the old one's data structures, so it
+ * MUST NOT be used after the old one is freed, and it MUST NOT be
+ * passed to sk_addr_free. (The latter is why it's returned by value
+ * rather than dynamically allocated - that should clue in anyone
+ * writing a call to it that something is weird about it.)
+ */
+static struct SockAddr_tag sk_extractaddr_tmp(
+    SockAddr addr, const SockAddrStep *step)
+{
+    struct SockAddr_tag toret;
+    toret = *addr;                    /* structure copy */
+    toret.refcount = 1;
+
+#ifndef NO_IPV6
+    toret.ais = step->ai;
+#endif
+    if (SOCKADDR_FAMILY(addr, *step) == AF_INET
+#ifndef NO_IPV6
+        && !toret.ais
+#endif
+        )
+        toret.addresses += step->curraddr;
+
+    return toret;
+}
+
 int sk_addr_needs_port(SockAddr addr)
 {
     return addr->namedpipe ? FALSE : TRUE;
@@ -986,7 +1015,11 @@ static DWORD try_connect(Actual_Socket sock,
         p_closesocket(sock->s);
     }
 
-    plug_log(sock->plug, 0, sock->addr, sock->port, NULL, 0);
+    {
+        struct SockAddr_tag thisaddr = sk_extractaddr_tmp(
+            sock->addr, &sock->step);
+        plug_log(sock->plug, 0, &thisaddr, sock->port, NULL, 0);
+    }
 
     /*
      * Open socket.
@@ -1203,10 +1236,11 @@ static DWORD try_connect(Actual_Socket sock,
      */
     add234(sktree, sock);
 
-    if (err)
-	{
-	plug_log(sock->plug, 1, sock->addr, sock->port, sock->error, err);
-	}
+    if (err) {
+        struct SockAddr_tag thisaddr = sk_extractaddr_tmp(
+            sock->addr, &sock->step);
+	plug_log(sock->plug, 1, &thisaddr, sock->port, sock->error, err);
+    }
     return err;
 }
 
@@ -1693,9 +1727,11 @@ int select_result(WPARAM wParam, LPARAM lParam)
 	 * plug.
 	 */
 	if (s->addr) {
-	    plug_log(s->plug, 1, s->addr, s->port,
+            struct SockAddr_tag thisaddr = sk_extractaddr_tmp(
+                s->addr, &s->step);
+	    plug_log(s->plug, 1, &thisaddr, s->port,
 		     winsock_error_string(err), err);
-	    while (s->addr && sk_nextaddr(s->addr, &s->step)) {
+	    while (err && s->addr && sk_nextaddr(s->addr, &s->step)) {
 		err = try_connect(s
 #ifdef MPEXT
 		    , 0, 0
@@ -1880,9 +1916,9 @@ static char *sk_tcp_peer_info(Socket sock)
     struct sockaddr_in addr;
 #else
     struct sockaddr_storage addr;
+    char buf[INET6_ADDRSTRLEN];
 #endif
     int addrlen = sizeof(addr);
-    char buf[INET6_ADDRSTRLEN];
 
     if (p_getpeername(s->s, (struct sockaddr *)&addr, &addrlen) < 0)
         return NULL;

+ 10 - 74
source/putty/windows/winpgntc.c

@@ -4,15 +4,16 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <assert.h>
 
 #include "putty.h"
+#include "pageant.h" /* for AGENT_MAX_MSGLEN */
 
 #ifndef NO_SECURITY
 #include "winsecur.h"
 #endif
 
 #define AGENT_COPYDATA_ID 0x804e50ba   /* random goop */
-#define AGENT_MAX_MSGLEN  8192
 
 int agent_exists(void)
 {
@@ -24,54 +25,14 @@ int agent_exists(void)
 	return TRUE;
 }
 
-/*
- * Unfortunately, this asynchronous agent request mechanism doesn't
- * appear to work terribly well. I'm going to comment it out for
- * the moment, and see if I can come up with a better one :-/
- */
-#ifdef WINDOWS_ASYNC_AGENT
-
-struct agent_query_data {
-    COPYDATASTRUCT cds;
-    unsigned char *mapping;
-    HANDLE handle;
-    char *mapname;
-    HWND hwnd;
-    void (*callback)(void *, void *, int);
-    void *callback_ctx;
-};
-
-DWORD WINAPI agent_query_thread(LPVOID param)
+void agent_cancel_query(agent_pending_query *q)
 {
-    struct agent_query_data *data = (struct agent_query_data *)param;
-    unsigned char *ret;
-    int id, retlen;
-
-    id = SendMessage(data->hwnd, WM_COPYDATA, (WPARAM) NULL,
-		     (LPARAM) &data->cds);
-    ret = NULL;
-    if (id > 0) {
-	retlen = 4 + GET_32BIT(data->mapping);
-	ret = snewn(retlen, unsigned char);
-	if (ret) {
-	    memcpy(ret, data->mapping, retlen);
-	}
-    }
-    if (!ret)
-	retlen = 0;
-    UnmapViewOfFile(data->mapping);
-    CloseHandle(data->handle);
-    sfree(data->mapname);
-
-    agent_schedule_callback(data->callback, data->callback_ctx, ret, retlen);
-
-    return 0;
+    assert(0 && "Windows agent queries are never asynchronous!");
 }
 
-#endif
-
-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)
 {
     HWND hwnd;
     char *mapname;
@@ -88,7 +49,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;
@@ -129,38 +90,13 @@ 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);
     cds.dwData = AGENT_COPYDATA_ID;
     cds.cbData = 1 + strlen(mapname);
     cds.lpData = mapname;
-#ifdef WINDOWS_ASYNC_AGENT
-    if (callback != NULL && !(flags & FLAG_SYNCAGENT)) {
-	/*
-	 * We need an asynchronous Pageant request. Since I know of
-	 * no way to stop SendMessage from blocking the thread it's
-	 * called in, I see no option but to start a fresh thread.
-	 * When we're done we'll PostMessage the result back to our
-	 * main window, so that the callback is done in the primary
-	 * thread to avoid concurrency.
-	 */
-	struct agent_query_data *data = snew(struct agent_query_data);
-	DWORD threadid;
-	data->mapping = p;
-	data->handle = filemap;
-	data->mapname = mapname;
-	data->callback = callback;
-	data->callback_ctx = callback_ctx;
-	data->cds = cds;	       /* structure copy */
-	data->hwnd = hwnd;
-	if (CreateThread(NULL, 0, agent_query_thread, data, 0, &threadid))
-	    return 0;
-	sfree(mapname);
-	sfree(data);
-    }
-#endif
 
     /*
      * The user either passed a null callback (indicating that the
@@ -182,5 +118,5 @@ int agent_query(void *in, int inlen, void **out, int *outlen,
     sfree(mapname);
     if (psd)
         LocalFree(psd);
-    return 1;
+    return NULL;
 }

+ 45 - 17
source/putty/windows/winsecur.c

@@ -102,17 +102,17 @@ PSID get_user_sid(void)
     return ret;
 }
 
-int getsids(char *error)
+int getsids(char **error)
 {
     SID_IDENTIFIER_AUTHORITY world_auth = SECURITY_WORLD_SID_AUTHORITY;
     SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY;
     int ret;
 
-    error=NULL;
+    *error = NULL;
 
     if (!usersid) {
         if ((usersid = get_user_sid()) == NULL) {
-            error = dupprintf("unable to construct SID for current user: %s",
+            *error = dupprintf("unable to construct SID for current user: %s",
                                win_strerror(GetLastError()));
             goto cleanup;
         }
@@ -121,7 +121,7 @@ int getsids(char *error)
     if (!worldsid) {
         if (!AllocateAndInitializeSid(&world_auth, 1, SECURITY_WORLD_RID,
                                       0, 0, 0, 0, 0, 0, 0, &worldsid)) {
-            error = dupprintf("unable to construct SID for world: %s",
+            *error = dupprintf("unable to construct SID for world: %s",
                                win_strerror(GetLastError()));
             goto cleanup;
         }
@@ -130,20 +130,16 @@ int getsids(char *error)
     if (!networksid) {
         if (!AllocateAndInitializeSid(&nt_auth, 1, SECURITY_NETWORK_RID,
                                       0, 0, 0, 0, 0, 0, 0, &networksid)) {
-            error = dupprintf("unable to construct SID for "
+            *error = dupprintf("unable to construct SID for "
                                "local same-user access only: %s",
                                win_strerror(GetLastError()));
             goto cleanup;
         }
     }
 
-    ret=TRUE;
+    ret = TRUE;
 
  cleanup:
-    if (ret) {
-      sfree(error);
-      error = NULL;
-    }
     return ret;
 }
   
@@ -162,7 +158,7 @@ int make_private_security_descriptor(DWORD permissions,
     *acl = NULL;
     *error = NULL;
 
-    if (!getsids(*error))
+    if (!getsids(error))
       goto cleanup;
 
     memset(ea, 0, sizeof(ea));
@@ -234,7 +230,7 @@ int make_private_security_descriptor(DWORD permissions,
     return ret;
 }
 
-int setprocessacl(char *error)
+static int really_restrict_process_acl(char **error)
 {
     EXPLICIT_ACCESS ea[2];
     int acl_err;
@@ -270,8 +266,8 @@ int setprocessacl(char *error)
     acl_err = p_SetEntriesInAclA(2, ea, NULL, &acl);
 
     if (acl_err != ERROR_SUCCESS || acl == NULL) {
-	error = dupprintf("unable to construct ACL: %s",
-			  win_strerror(acl_err));
+	*error = dupprintf("unable to construct ACL: %s",
+                           win_strerror(acl_err));
         goto cleanup;
     }
 
@@ -279,8 +275,8 @@ int setprocessacl(char *error)
         (GetCurrentProcess(), SE_KERNEL_OBJECT,
          OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
          usersid, NULL, acl, NULL)) {
-	error=dupprintf("Unable to set process ACL: %s",
-			win_strerror(GetLastError()));
+	*error = dupprintf("Unable to set process ACL: %s",
+                           win_strerror(GetLastError()));
 	goto cleanup;
     }
 		      
@@ -295,5 +291,37 @@ int setprocessacl(char *error)
         }
     }
     return ret;
-}  
+}
 #endif /* !defined NO_SECURITY */
+
+/*
+ * Lock down our process's ACL, to present an obstacle to malware
+ * trying to write into its memory. This can't be a full defence,
+ * because well timed malware could attack us before this code runs -
+ * even if it was unconditionally run at the very start of main(),
+ * which we wouldn't want to do anyway because it turns out in practie
+ * that interfering with other processes in this way has significant
+ * non-infringing uses on Windows (e.g. screen reader software).
+ *
+ * If we've been requested to do this and are unsuccessful, bomb out
+ * via modalfatalbox rather than continue in a less protected mode.
+ *
+ * This function is intentionally outside the #ifndef NO_SECURITY that
+ * covers the rest of this file, because when PuTTY is compiled
+ * without the ability to restrict its ACL, we don't want it to
+ * silently pretend to honour the instruction to do so.
+ */
+void restrict_process_acl(void)
+{
+    char *error = NULL;
+    int ret;
+
+#if !defined NO_SECURITY
+    ret = really_restrict_process_acl(&error);
+#else
+    ret = FALSE;
+    error = dupstr("ACL restrictions not compiled into this binary");
+#endif
+    if (!ret)
+        modalfatalbox("Could not restrict process ACL: %s", error);
+}

+ 0 - 2
source/putty/windows/winsecur.h

@@ -56,6 +56,4 @@ int make_private_security_descriptor(DWORD permissions,
                                      PACL *acl,
                                      char **error);
 
-int setprocessacl(char *error);
-
 #endif

+ 4 - 0
source/putty/windows/winstuff.h

@@ -29,6 +29,8 @@
 #include "winhelp.h"
 #endif
 
+#define BUILDINFO_PLATFORM "Windows"
+
 struct Filename {
     char *path;
 };
@@ -489,6 +491,7 @@ void dll_hijacking_protection(void);
 BOOL init_winver(void);
 HMODULE load_system32_dll(const char *libname);
 const char *win_strerror(int error);
+void restrict_process_acl(void);
 
 /*
  * Exports from sizetip.c.
@@ -554,6 +557,7 @@ extern Backend serial_backend;
 void add_session_to_jumplist(const char * const sessionname);
 void remove_session_from_jumplist(const char * const sessionname);
 void clear_jumplist(void);
+BOOL set_explicit_app_user_model_id();
 
 /*
  * Extra functions in winstore.c over and above the interface in