Pārlūkot izejas kodu

PuTTY 0.74

(cherry picked from commit bdb218e2811e27bdddcc14ecf53c9f5906bfe691)

Source commit: 74b747e0ae4aa934eddccbbd083e7938866c3799
Martin Prikryl 5 gadi atpakaļ
vecāks
revīzija
bdec885e89
48 mainītis faili ar 408 papildinājumiem un 222 dzēšanām
  1. 27 0
      source/putty/defs.h
  2. 21 0
      source/putty/doc/config.but
  3. 1 1
      source/putty/doc/copy.but
  4. 3 1
      source/putty/doc/feedback.but
  5. 1 1
      source/putty/doc/licence.but
  6. 9 9
      source/putty/doc/plink.but
  7. 2 2
      source/putty/doc/pscp.but
  8. 3 1
      source/putty/import.c
  9. 5 4
      source/putty/logging.c
  10. 2 0
      source/putty/marshal.h
  11. 5 3
      source/putty/memory.c
  12. 19 29
      source/putty/misc.c
  13. 9 24
      source/putty/misc.h
  14. 44 17
      source/putty/mpint.c
  15. 5 1
      source/putty/mpint_i.h
  16. 2 1
      source/putty/network.h
  17. 1 1
      source/putty/proxy.c
  18. 12 22
      source/putty/putty.h
  19. 3 2
      source/putty/ssh.c
  20. 9 6
      source/putty/ssh.h
  21. 1 0
      source/putty/ssh1bpp.c
  22. 5 2
      source/putty/ssh1connection.c
  23. 14 9
      source/putty/ssh1login.c
  24. 1 0
      source/putty/ssh2bpp-bare.c
  25. 1 0
      source/putty/ssh2bpp.c
  26. 5 1
      source/putty/ssh2connection-client.c
  27. 3 1
      source/putty/ssh2connection.c
  28. 21 10
      source/putty/ssh2transport.c
  29. 27 16
      source/putty/ssh2userauth.c
  30. 36 4
      source/putty/sshcommon.c
  31. 3 1
      source/putty/sshdss.c
  32. 1 1
      source/putty/sshecc.c
  33. 1 1
      source/putty/sshgssc.c
  34. 3 3
      source/putty/sshhmac.c
  35. 8 0
      source/putty/sshppl.h
  36. 4 4
      source/putty/sshprng.c
  37. 4 4
      source/putty/sshpubk.c
  38. 21 3
      source/putty/sshrsa.c
  39. 7 6
      source/putty/sshshare.c
  40. 1 2
      source/putty/sshverstring.c
  41. 1 1
      source/putty/tree234.c
  42. 38 4
      source/putty/utils.c
  43. 5 5
      source/putty/version.h
  44. 1 1
      source/putty/windows/wingss.c
  45. 1 1
      source/putty/windows/winmisc.c
  46. 1 2
      source/putty/windows/winnet.c
  47. 10 14
      source/putty/windows/winstore.c
  48. 1 1
      source/putty/windows/winstuff.h

+ 27 - 0
source/putty/defs.h

@@ -22,11 +22,38 @@
 #define PRIdMAX "I64d"
 #define PRIXMAX "I64X"
 #define SCNu64 "I64u"
+#define SIZEx "Ix"
+#define SIZEu "Iu"
 uintmax_t strtoumax(const char *nptr, char **endptr, int base);
 #else
 #include <inttypes.h>
+/* Because we still support older MSVC libraries which don't recognise the
+ * standard C "z" modifier for size_t-sized integers, we must use an
+ * inttypes.h-style macro for those */
+#define SIZEx "zx"
+#define SIZEu "zu"
 #endif
 
+#if defined __GNUC__ || defined __clang__
+/*
+ * On MinGW, the correct compiler format checking for vsnprintf() etc
+ * can depend on compile-time flags; these control whether you get
+ * ISO C or Microsoft's non-standard format strings.
+ * We sometimes use __attribute__ ((format)) for our own printf-like
+ * functions, which are ultimately interpreted by the toolchain-chosen
+ * printf, so we need to take that into account to get correct warnings.
+ */
+#ifdef __MINGW_PRINTF_FORMAT
+#define PRINTF_LIKE(fmt_index, ellipsis_index) \
+    __attribute__ ((format (__MINGW_PRINTF_FORMAT, fmt_index, ellipsis_index)))
+#else
+#define PRINTF_LIKE(fmt_index, ellipsis_index) \
+    __attribute__ ((format (printf, fmt_index, ellipsis_index)))
+#endif
+#else /* __GNUC__ */
+#define PRINTF_LIKE(fmt_index, ellipsis_index)
+#endif /* __GNUC__ */
+
 typedef struct conf_tag Conf;
 typedef struct terminal_tag Terminal;
 typedef struct term_utf8_decode term_utf8_decode;

+ 21 - 0
source/putty/doc/config.but

@@ -2544,6 +2544,27 @@ If the first key type PuTTY finds is below the \q{warn below here}
 line, you will see a warning box when you make the connection, similar
 to that for cipher selection (see \k{config-ssh-encryption}).
 
+\S{config-ssh-prefer-known-hostkeys} Preferring known host keys
+
+By default, PuTTY will adjust the preference order for host key
+algorithms so that any host keys it already knows are moved to the top
+of the list.
+
+This prevents you from having to check and confirm a new host key for
+a server you already had one for (e.g. because the server has
+generated an alternative key of a type higher in PuTTY's preference
+order, or because you changed the preference order itself).
+
+However, on the other hand, it can leak information to a listener in
+the network about \e{whether} you already know a host key for this
+server.
+
+For this reason, this policy is configurable. By turning this checkbox
+off, you can reset PuTTY to always use the exact order of host key
+algorithms configured in the preference list described in
+\k{config-ssh-hostkey-order}, so that a listener will find out nothing
+about what keys you had stored.
+
 \S{config-ssh-kex-manual-hostkeys} \ii{Manually configuring host keys}
 
 In some situations, if PuTTY's automated host key management is not

+ 1 - 1
source/putty/doc/copy.but

@@ -1,5 +1,5 @@
 \# Generated by licence.pl from LICENCE.
 \# You should edit those files rather than editing this one.
 
-\define{shortcopyrightdetails} 1997-2019 Simon Tatham
+\define{shortcopyrightdetails} 1997-2020 Simon Tatham
 

+ 3 - 1
source/putty/doc/feedback.but

@@ -51,7 +51,9 @@ actually need it, and only one of us needs to download it instead of
 it being automatically copied to all the developers.
 
 (If the file contains confidential information, then you could encrypt
-it with our Secure Contact Key; see \k{pgpkeys-pubkey} for details.)
+it with our Secure Contact Key; see \k{pgpkeys-pubkey} for details.
+Please \e{only} use this for information that \e{needs} to be
+confidential.)
 
 Some people like to send mail in MS Word format. Please \e{don't}
 send us bug reports, or any other mail, as a Word document. Word

+ 1 - 1
source/putty/doc/licence.but

@@ -3,7 +3,7 @@
 
 \A{licence} PuTTY \ii{Licence}
 
-PuTTY is \i{copyright} 1997-2019 Simon Tatham.
+PuTTY is \i{copyright} 1997-2020 Simon Tatham.
 
 Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, Colin Watson, Christopher Staite, Lorenz Diener, Christian Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav Kuzmich, Nico Williams, Viktor Dukhovni, and CORE SDI S.A.
 

+ 9 - 9
source/putty/doc/plink.but

@@ -39,9 +39,9 @@ Once you've got a console window to type into, you can just type
 version of Plink you're using, and gives you a brief summary of how to
 use Plink:
 
-\c Z:\sysosd>plink
+\c C:\>plink
 \c Plink: command-line connection utility
-\c Release 0.73
+\c Release 0.74
 \c Usage: plink [options] [user@]host [command]
 \c        ("host" can also be a PuTTY saved session name)
 \c Options:
@@ -100,7 +100,7 @@ Once this works, you are ready to use Plink.
 To make a simple interactive connection to a remote server, just
 type \c{plink} and then the host name:
 
-\c Z:\sysosd>plink login.example.com
+\c C:\>plink login.example.com
 \c
 \c Debian GNU/Linux 2.2 flunky.example.com
 \c flunky login:
@@ -117,7 +117,7 @@ In order to connect with a different protocol, you can give the
 command line options \c{-ssh}, \c{-telnet}, \c{-rlogin} or \c{-raw}.
 To make an SSH connection, for example:
 
-\c Z:\sysosd>plink -ssh login.example.com
+\c C:\>plink -ssh login.example.com
 \c login as:
 
 If you have already set up a PuTTY saved session, then instead of
@@ -125,7 +125,7 @@ supplying a host name, you can give the saved session name. This
 allows you to use public-key authentication, specify a user name,
 and use most of the other features of PuTTY:
 
-\c Z:\sysosd>plink my-ssh-session
+\c C:\>plink my-ssh-session
 \c Sent username "fred"
 \c Authenticating with public key "fred@winbox"
 \c Last login: Thu Dec  6 19:25:33 2001 from :0.0
@@ -196,18 +196,18 @@ Once you have done all this, you should be able to run a remote
 command on the SSH server machine and have it execute automatically
 with no prompting:
 
-\c Z:\sysosd>plink login.example.com -l fred echo hello, world
+\c C:\>plink login.example.com -l fred echo hello, world
 \c hello, world
 \c
-\c Z:\sysosd>
+\c C:\>
 
 Or, if you have set up a saved session with all the connection
 details:
 
-\c Z:\sysosd>plink mysession echo hello, world
+\c C:\>plink mysession echo hello, world
 \c hello, world
 \c
-\c Z:\sysosd>
+\c C:\>
 
 Then you can set up other programs to run this Plink command and
 talk to it as if it were a process on the server machine.

+ 2 - 2
source/putty/doc/pscp.but

@@ -37,9 +37,9 @@ Once you've got a console window to type into, you can just type
 version of PSCP you're using, and gives you a brief summary of how to
 use PSCP:
 
-\c Z:\owendadmin>pscp
+\c C:\>pscp
 \c PuTTY Secure Copy client
-\c Release 0.73
+\c Release 0.74
 \c Usage: pscp [options] [user@]host:source target
 \c        pscp [options] source [source...] [user@]host:target
 \c        pscp [options] -ls [user@]host:filespec

+ 3 - 1
source/putty/import.c

@@ -538,8 +538,10 @@ static ssh2_userkey *openssh_pem_read(
     strbuf *blob = strbuf_new_nm();
     int privptr = 0, publen;
 
-    if (!key)
+    if (!key) {
+        strbuf_free(blob);
         return NULL;
+    }
 
     if (key->encrypted) {
         unsigned char keybuf[32];

+ 5 - 4
source/putty/logging.c

@@ -58,7 +58,7 @@ static void logwrite(LogContext *ctx, ptrlen data)
  * Convenience wrapper on logwrite() which printf-formats the
  * string.
  */
-static void logprintf(LogContext *ctx, const char *fmt, ...)
+static PRINTF_LIKE(2, 3) void logprintf(LogContext *ctx, const char *fmt, ...)
 {
     va_list ap;
     char *data;
@@ -344,7 +344,7 @@ void log_packet(LogContext *ctx, int direction, int type,
         /* If we're about to stop omitting, it's time to say how
          * much we omitted. */
         if ((blktype != PKTLOG_OMIT) && omitted) {
-            logprintf(ctx, "  (%d byte%s omitted)\r\n",
+            logprintf(ctx, "  (%"SIZEu" byte%s omitted)\r\n",
                       omitted, (omitted==1?"":"s"));
             omitted = 0;
         }
@@ -352,7 +352,8 @@ void log_packet(LogContext *ctx, int direction, int type,
         /* (Re-)initialise dumpdata as necessary
          * (start of row, or if we've just stopped omitting) */
         if (!output_pos && !omitted)
-            sprintf(dumpdata, "  %08zx%*s\r\n", p-(p%16), 1+3*16+2+16, "");
+            sprintf(dumpdata, "  %08"SIZEx"%*s\r\n",
+                    p-(p%16), 1+3*16+2+16, "");
 
         /* Deal with the current byte. */
         if (blktype == PKTLOG_OMIT) {
@@ -387,7 +388,7 @@ void log_packet(LogContext *ctx, int direction, int type,
 
     /* Tidy up */
     if (omitted)
-        logprintf(ctx, "  (%d byte%s omitted)\r\n",
+        logprintf(ctx, "  (%"SIZEu" byte%s omitted)\r\n",
                   omitted, (omitted==1?"":"s"));
     logflush(ctx);
 }

+ 2 - 0
source/putty/marshal.h

@@ -286,6 +286,8 @@ static inline void BinarySource_INIT__(BinarySource *src, ptrlen data)
     BinarySource_get_rsa_ssh1_pub(BinarySource_UPCAST(src), rsa, order)
 #define get_rsa_ssh1_priv(src, rsa) \
     BinarySource_get_rsa_ssh1_priv(BinarySource_UPCAST(src), rsa)
+#define get_rsa_ssh1_priv_agent(src) \
+    BinarySource_get_rsa_ssh1_priv_agent(BinarySource_UPCAST(src))
 
 #define get_err(src) (BinarySource_UPCAST(src)->err)
 #define get_avail(src) (BinarySource_UPCAST(src)->len - \

+ 5 - 3
source/putty/memory.c

@@ -121,9 +121,11 @@ void *safegrowarray(void *ptr, size_t *allocated, size_t eltsize,
     void *toret;
     if (secret) {
         toret = safemalloc(newsize, eltsize, 0);
-        memcpy(toret, ptr, oldsize * eltsize);
-        smemclr(ptr, oldsize * eltsize);
-        sfree(ptr);
+        if (oldsize) {
+            memcpy(toret, ptr, oldsize * eltsize);
+            smemclr(ptr, oldsize * eltsize);
+            sfree(ptr);
+        }
     } else {
         toret = saferealloc(ptr, newsize, eltsize);
     }

+ 19 - 29
source/putty/misc.c

@@ -51,43 +51,29 @@ void add_prompt(prompts_t *p, char *promptstr, bool echo)
     prompt_t *pr = snew(prompt_t);
     pr->prompt = promptstr;
     pr->echo = echo;
-    pr->result = NULL;
-    pr->resultsize = 0;
+    pr->result = strbuf_new_nm();
     sgrowarray(p->prompts, p->prompts_size, p->n_prompts);
     p->prompts[p->n_prompts++] = pr;
 }
-void prompt_ensure_result_size(prompt_t *pr, int newlen)
+void prompt_set_result(prompt_t *pr, const char *newstr)
 {
-    if ((int)pr->resultsize < newlen) {
-        char *newbuf;
-        newlen = newlen * 5 / 4 + 512; /* avoid too many small allocs */
-
-        /*
-         * We don't use sresize / realloc here, because we will be
-         * storing sensitive stuff like passwords in here, and we want
-         * to make sure that the data doesn't get copied around in
-         * memory without the old copy being destroyed.
-         */
-        newbuf = snewn(newlen, char);
-        memcpy(newbuf, pr->result, pr->resultsize);
-        smemclr(pr->result, pr->resultsize);
-        sfree(pr->result);
-        pr->result = newbuf;
-        pr->resultsize = newlen;
-    }
+    strbuf_clear(pr->result);
+    put_datapl(pr->result, ptrlen_from_asciz(newstr));
 }
-void prompt_set_result(prompt_t *pr, const char *newstr)
+const char *prompt_get_result_ref(prompt_t *pr)
+{
+    return pr->result->s;
+}
+char *prompt_get_result(prompt_t *pr)
 {
-    prompt_ensure_result_size(pr, strlen(newstr) + 1);
-    strcpy(pr->result, newstr);
+    return dupstr(pr->result->s);
 }
 void free_prompts(prompts_t *p)
 {
     size_t i;
     for (i=0; i < p->n_prompts; i++) {
         prompt_t *pr = p->prompts[i];
-        smemclr(pr->result, pr->resultsize); /* burn the evidence */
-        sfree(pr->result);
+        strbuf_free(pr->result);
         sfree(pr->prompt);
         sfree(pr);
     }
@@ -237,22 +223,26 @@ char *buildinfo(const char *newline)
 #else
     strbuf_catf(buf, ", emulating ");
 #endif
-    strbuf_catf(buf, "Visual Studio", newline);
+    strbuf_catf(buf, "Visual Studio");
 
 #if 0
     /*
      * List of _MSC_VER values and their translations taken from
      * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
-     * except for 1920, which is not yet listed on that page as of
-     * 2019-03-22, and was determined experimentally by Sean Kain.
      *
      * The pointless #if 0 branch containing this comment is there so
      * that every real clause can start with #elif and there's no
      * anomalous first clause. That way the patch looks nicer when you
      * add extra ones.
      */
+#elif _MSC_VER == 1923
+    strbuf_catf(buf, " 2019 (16.3)");
+#elif _MSC_VER == 1922
+    strbuf_catf(buf, " 2019 (16.2)");
+#elif _MSC_VER == 1921
+    strbuf_catf(buf, " 2019 (16.1)");
 #elif _MSC_VER == 1920
-    strbuf_catf(buf, " 2019 (16.x)");
+    strbuf_catf(buf, " 2019 (16.0)");
 #elif _MSC_VER == 1916
     strbuf_catf(buf, " 2017 version 15.9");
 #elif _MSC_VER == 1915

+ 9 - 24
source/putty/misc.h

@@ -24,29 +24,10 @@ char *host_strchr(const char *s, int c);
 char *host_strrchr(const char *s, int c);
 char *host_strduptrim(const char *s);
 
-#ifdef __GNUC__
-/*
- * On MinGW, the correct compiler format checking for vsnprintf() etc
- * can depend on compile-time flags; these control whether you get
- * ISO C or Microsoft's non-standard format strings.
- * We sometimes use __attribute__ ((format)) for our own printf-like
- * functions, which are ultimately interpreted by the toolchain-chosen
- * printf, so we need to take that into account to get correct warnings.
- */
-#ifdef __MINGW_PRINTF_FORMAT
-#define PUTTY_PRINTF_ARCHETYPE __MINGW_PRINTF_FORMAT
-#else
-#define PUTTY_PRINTF_ARCHETYPE printf
-#endif
-#endif /* __GNUC__ */
-
 char *dupstr(const char *s);
-char *dupcat(const char *s1, ...);
-char *dupprintf(const char *fmt, ...)
-#ifdef __GNUC__
-    __attribute__ ((format (PUTTY_PRINTF_ARCHETYPE, 1, 2)))
-#endif
-    ;
+char *dupcat_fn(const char *s1, ...);
+#define dupcat(...) dupcat_fn(__VA_ARGS__, (const char *)NULL)
+char *dupprintf(const char *fmt, ...) PRINTF_LIKE(1, 2);
 char *dupvprintf(const char *fmt, va_list ap);
 void burnstr(char *string);
 
@@ -72,9 +53,13 @@ strbuf *strbuf_new_nm(void);
 
 void strbuf_free(strbuf *buf);
 void *strbuf_append(strbuf *buf, size_t len);
+void strbuf_shrink_to(strbuf *buf, size_t new_len);
+void strbuf_shrink_by(strbuf *buf, size_t amount_to_remove);
 char *strbuf_to_str(strbuf *buf); /* does free buf, but you must free result */
-void strbuf_catf(strbuf *buf, const char *fmt, ...);
+void strbuf_catf(strbuf *buf, const char *fmt, ...) PRINTF_LIKE(2, 3);
 void strbuf_catfv(strbuf *buf, const char *fmt, va_list ap);
+static inline void strbuf_clear(strbuf *buf) { strbuf_shrink_to(buf, 0); }
+bool strbuf_chomp(strbuf *buf, char char_to_remove);
 
 strbuf *strbuf_new_for_agent_query(void);
 void strbuf_finalise_agent_query(strbuf *buf);
@@ -258,7 +243,7 @@ static inline NORETURN void unreachable_internal(void) { abort(); }
  */
 
 #ifdef DEBUG
-void debug_printf(const char *fmt, ...);
+void debug_printf(const char *fmt, ...) PRINTF_LIKE(1, 2);
 void debug_memdump(const void *buf, int len, bool L);
 #define debug(...) (debug_printf(__VA_ARGS__))
 #define dmemdump(buf,len) (debug_memdump(buf, len, false))

+ 44 - 17
source/putty/mpint.c

@@ -33,6 +33,35 @@ static inline BignumInt mp_word(mp_int *x, size_t i)
     return i < x->nw ? x->w[i] : 0;
 }
 
+/*
+ * Shift an ordinary C integer by BIGNUM_INT_BITS, in a way that
+ * avoids writing a shift operator whose RHS is greater or equal to
+ * the size of the type, because that's undefined behaviour in C.
+ *
+ * In fact we must avoid even writing it in a definitely-untaken
+ * branch of an if, because compilers will sometimes warn about
+ * that. So you can't just write 'shift too big ? 0 : n >> shift',
+ * because even if 'shift too big' is a constant-expression
+ * evaluating to false, you can still get complaints about the
+ * else clause of the ?:.
+ *
+ * So we have to re-check _inside_ that clause, so that the shift
+ * count is reset to something nonsensical but safe in the case
+ * where the clause wasn't going to be taken anyway.
+ */
+static uintmax_t shift_right_by_one_word(uintmax_t n)
+{
+    bool shift_too_big = BIGNUM_INT_BYTES >= sizeof(n);
+    return shift_too_big ? 0 :
+        n >> (shift_too_big ? 0 : BIGNUM_INT_BITS);
+}
+static uintmax_t shift_left_by_one_word(uintmax_t n)
+{
+    bool shift_too_big = BIGNUM_INT_BYTES >= sizeof(n);
+    return shift_too_big ? 0 :
+        n << (shift_too_big ? 0 : BIGNUM_INT_BITS);
+}
+
 static mp_int *mp_make_sized(size_t nw)
 {
     mp_int *x = snew_plus(mp_int, nw * sizeof(BignumInt));
@@ -260,12 +289,8 @@ unsigned mp_get_bit(mp_int *x, size_t bit)
 uintmax_t mp_get_integer(mp_int *x)
 {
     uintmax_t toret = 0;
-    for (size_t i = x->nw; i-- > 0 ;) {
-        /* Shift in two stages to avoid undefined behaviour if the
-         * shift count equals the integer width */
-        toret = (toret << (BIGNUM_INT_BITS/2)) << (BIGNUM_INT_BITS/2);
-        toret |= x->w[i];
-    }
+    for (size_t i = x->nw; i-- > 0 ;)
+        toret = shift_left_by_one_word(toret) | x->w[i];
     return toret;
 }
 
@@ -757,8 +782,8 @@ static BignumCarry mp_add_masked_integer_into(
 {
     for (size_t i = 0; i < rw; i++) {
         BignumInt aword = mp_word(a, i);
-        size_t shift = i * BIGNUM_INT_BITS;
-        BignumInt bword = shift < BIGNUM_INT_BYTES ? b >> shift : 0;
+        BignumInt bword = b;
+        b = shift_right_by_one_word(b);
         BignumInt out;
         bword = (bword ^ b_xor) & b_and;
         BignumADC(out, carry, aword, bword, carry);
@@ -799,7 +824,7 @@ static void mp_add_integer_into_shifted_by_words(
          * shift n down. If it's 0, we add zero bits into r, and
          * leave n alone. */
         BignumInt bword = n & -(BignumInt)indicator;
-        uintmax_t new_n = (BIGNUM_INT_BITS < 64 ? n >> BIGNUM_INT_BITS : 0);
+        uintmax_t new_n = shift_right_by_one_word(n);
         n ^= (n ^ new_n) & -(uintmax_t)indicator;
 
         BignumInt aword = mp_word(a, i);
@@ -844,11 +869,12 @@ unsigned mp_cmp_hs(mp_int *a, mp_int *b)
 unsigned mp_hs_integer(mp_int *x, uintmax_t n)
 {
     BignumInt carry = 1;
-    for (size_t i = 0; i < x->nw; i++) {
-        size_t shift = i * BIGNUM_INT_BITS;
-        BignumInt nword = shift < CHAR_BIT*sizeof(n) ? n >> shift : 0;
+    size_t nwords = sizeof(n)/BIGNUM_INT_BYTES;
+    for (size_t i = 0, e = size_t_max(x->nw, nwords); i < e; i++) {
+        BignumInt nword = n;
+        n = shift_right_by_one_word(n);
         BignumInt dummy_out;
-        BignumADC(dummy_out, carry, x->w[i], ~nword, carry);
+        BignumADC(dummy_out, carry, mp_word(x, i), ~nword, carry);
         (void)dummy_out;
     }
     return carry;
@@ -870,10 +896,11 @@ unsigned mp_cmp_eq(mp_int *a, mp_int *b)
 unsigned mp_eq_integer(mp_int *x, uintmax_t n)
 {
     BignumInt diff = 0;
-    for (size_t i = 0; i < x->nw; i++) {
-        size_t shift = i * BIGNUM_INT_BITS;
-        BignumInt nword = shift < CHAR_BIT*sizeof(n) ? n >> shift : 0;
-        diff |= x->w[i] ^ nword;
+    size_t nwords = sizeof(n)/BIGNUM_INT_BYTES;
+    for (size_t i = 0, e = size_t_max(x->nw, nwords); i < e; i++) {
+        BignumInt nword = n;
+        n = shift_right_by_one_word(n);
+        diff |= mp_word(x, i) ^ nword;
     }
     return 1 ^ normalise_to_1(diff);   /* return 1 if diff _is_ zero */
 }

+ 5 - 1
source/putty/mpint_i.h

@@ -59,7 +59,11 @@
 
 /* You can lower the BignumInt size by defining BIGNUM_OVERRIDE on the
  * command line to be your chosen max value of BIGNUM_INT_BITS_BITS */
-#define BB_OK(b) (!defined BIGNUM_OVERRIDE || BIGNUM_OVERRIDE >= b)
+#if defined BIGNUM_OVERRIDE
+#define BB_OK(b) ((b) <= BIGNUM_OVERRIDE)
+#else
+#define BB_OK(b) (1)
+#endif
 
 #if defined __SIZEOF_INT128__ && BB_OK(6)
 

+ 2 - 1
source/putty/network.h

@@ -265,7 +265,8 @@ char *get_hostname(void);
  * Trivial socket implementation which just stores an error. Found in
  * errsock.c.
  */
-Socket *new_error_socket_fmt(Plug *plug, const char *fmt, ...);
+Socket *new_error_socket_fmt(Plug *plug, const char *fmt, ...)
+    PRINTF_LIKE(2, 3);
 
 /*
  * Trivial plug that does absolutely nothing. Found in nullplug.c.

+ 1 - 1
source/putty/proxy.c

@@ -1178,7 +1178,7 @@ int proxy_socks5_negotiate (ProxySocket *p, int change)
             switch (data[3]) {
               case 1: len += 4; break; /* IPv4 address */
               case 4: len += 16; break;/* IPv6 address */
-              case 3: len += (unsigned char)data[4]; break; /* domain name */
+              case 3: len += 1+(unsigned char)data[4]; break; /* domain name */
               default:
                 plug_closing(p->plug, "Proxy error: SOCKS proxy returned "
                              "unrecognised address format",

+ 12 - 22
source/putty/putty.h

@@ -636,19 +636,7 @@ GLOBAL char *cmdline_session_name;
 typedef struct {
     char *prompt;
     bool echo;
-    /*
-     * 'result' must be a dynamically allocated array of exactly
-     * 'resultsize' chars. The code for actually reading input may
-     * realloc it bigger (and adjust resultsize accordingly) if it has
-     * to. The caller should free it again when finished with it.
-     *
-     * If resultsize==0, then result may be NULL. When setting up a
-     * prompt_t, it's therefore easiest to initialise them this way,
-     * which means all actual allocation is done by the callee. This
-     * is what add_prompt does.
-     */
-    char *result;
-    size_t resultsize;
+    strbuf *result;
 } prompt_t;
 typedef struct {
     /*
@@ -679,11 +667,11 @@ typedef struct {
     void *data;         /* slot for housekeeping data, managed by
                          * seat_get_userpass_input(); initially NULL */
 } prompts_t;
-prompts_t *new_prompts();
+prompts_t *new_prompts(void);
 void add_prompt(prompts_t *p, char *promptstr, bool echo);
 void prompt_set_result(prompt_t *pr, const char *newstr);
-void prompt_ensure_result_size(prompt_t *pr, int len);
-/* Burn the evidence. (Assumes _all_ strings want free()ing.) */
+char *prompt_get_result(prompt_t *pr);
+const char *prompt_get_result_ref(prompt_t *pr);
 void free_prompts(prompts_t *p);
 
 /*
@@ -1015,7 +1003,7 @@ static inline bool seat_set_trust_status(Seat *seat, bool trusted)
 /* Unlike the seat's actual method, the public entry point
  * seat_connection_fatal is a wrapper function with a printf-like API,
  * defined in misc.c. */
-void seat_connection_fatal(Seat *seat, const char *fmt, ...);
+void seat_connection_fatal(Seat *seat, const char *fmt, ...) PRINTF_LIKE(2, 3);
 
 /* Handy aliases for seat_output which set is_stderr to a fixed value. */
 static inline size_t seat_stdout(Seat *seat, const void *data, size_t len)
@@ -1230,8 +1218,8 @@ static inline bool win_is_utf8(TermWin *win)
 /*
  * Global functions not specific to a connection instance.
  */
-void nonfatal(const char *, ...);
-NORETURN void modalfatalbox(const char *, ...);
+void nonfatal(const char *, ...) PRINTF_LIKE(1, 2);
+NORETURN void modalfatalbox(const char *, ...) PRINTF_LIKE(1, 2);
 NORETURN void cleanup_exit(int);
 
 /*
@@ -1268,6 +1256,7 @@ NORETURN void cleanup_exit(int);
     X(BOOL, NONE, compression) \
     X(INT, INT, ssh_kexlist) \
     X(INT, INT, ssh_hklist) \
+    X(BOOL, NONE, ssh_prefer_known_hostkeys) \
     X(INT, NONE, ssh_rekey_time) /* in minutes */ \
     X(STR, NONE, ssh_rekey_data) /* string encoding e.g. "100K", "2M", "1G" */ \
     X(BOOL, NONE, tryagent) \
@@ -1715,7 +1704,7 @@ void logfclose(LogContext *logctx);
 void logtraffic(LogContext *logctx, unsigned char c, int logmode);
 void logflush(LogContext *logctx);
 void logevent(LogContext *logctx, const char *event);
-void logeventf(LogContext *logctx, const char *fmt, ...);
+void logeventf(LogContext *logctx, const char *fmt, ...) PRINTF_LIKE(2, 3);
 void logeventvf(LogContext *logctx, const char *fmt, va_list ap);
 
 /*
@@ -1929,7 +1918,8 @@ bool is_interactive(void);
 void console_print_error_msg(const char *prefix, const char *msg);
 void console_print_error_msg_fmt_v(
     const char *prefix, const char *fmt, va_list ap);
-void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...);
+void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...)
+    PRINTF_LIKE(2, 3);
 
 /*
  * Exports from printing.c.
@@ -1967,7 +1957,7 @@ bool cmdline_host_ok(Conf *);
 #define TOOLTYPE_PORT_ARG 64
 extern int cmdline_tooltype;
 
-void cmdline_error(const char *, ...);
+void cmdline_error(const char *, ...) PRINTF_LIKE(1, 2);
 
 /*
  * Exports from config.c.

+ 3 - 2
source/putty/ssh.c

@@ -562,7 +562,7 @@ void ssh_deferred_abort_callback(void *vctx)
     Ssh *ssh = (Ssh *)vctx;
     char *msg = ssh->deferred_abort_message;
     ssh->deferred_abort_message = NULL;
-    ssh_sw_abort(ssh, msg);
+    ssh_sw_abort(ssh, "%s", msg);
     sfree(msg);
 }
 
@@ -996,7 +996,8 @@ static size_t ssh_sendbuffer(Backend *be)
 
     backlog = ssh_stdin_backlog(ssh->cl);
 
-    /* FIXME: also include sizes of pqs */
+    if (ssh->base_layer)
+        backlog += ssh_ppl_queued_data_size(ssh->base_layer);
 
     /*
      * If the SSH socket itself has backed up, add the total backup

+ 9 - 6
source/putty/ssh.h

@@ -52,6 +52,7 @@ struct ssh_channel;
 typedef struct PacketQueueNode PacketQueueNode;
 struct PacketQueueNode {
     PacketQueueNode *next, *prev;
+    size_t formal_size;    /* contribution to PacketQueueBase's total_size */
     bool on_free_queue;     /* is this packet scheduled for freeing? */
 };
 
@@ -84,6 +85,7 @@ typedef struct PktOut {
 
 typedef struct PacketQueueBase {
     PacketQueueNode end;
+    size_t total_size;    /* sum of all formal_size fields on the queue */
     struct IdempotentCallback *ic;
 } PacketQueueBase;
 
@@ -403,12 +405,12 @@ void ssh_conn_processed_data(Ssh *ssh);
 void ssh_check_frozen(Ssh *ssh);
 
 /* Functions to abort the connection, for various reasons. */
-void ssh_remote_error(Ssh *ssh, const char *fmt, ...);
-void ssh_remote_eof(Ssh *ssh, const char *fmt, ...);
-void ssh_proto_error(Ssh *ssh, const char *fmt, ...);
-void ssh_sw_abort(Ssh *ssh, const char *fmt, ...);
-void ssh_sw_abort_deferred(Ssh *ssh, const char *fmt, ...);
-void ssh_user_close(Ssh *ssh, const char *fmt, ...);
+void ssh_remote_error(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3);
+void ssh_remote_eof(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3);
+void ssh_proto_error(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3);
+void ssh_sw_abort(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3);
+void ssh_sw_abort_deferred(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3);
+void ssh_user_close(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3);
 
 /* Bit positions in the SSH-1 cipher protocol word */
 #define SSH1_CIPHER_IDEA        1
@@ -539,6 +541,7 @@ void BinarySource_get_rsa_ssh1_pub(
     BinarySource *src, RSAKey *result, RsaSsh1Order order);
 void BinarySource_get_rsa_ssh1_priv(
     BinarySource *src, RSAKey *rsa);
+RSAKey *BinarySource_get_rsa_ssh1_priv_agent(BinarySource *src);
 bool rsa_ssh1_encrypt(unsigned char *data, int length, RSAKey *key);
 mp_int *rsa_ssh1_decrypt(mp_int *input, RSAKey *key);
 bool rsa_ssh1_decrypt_pkcs1(mp_int *input, RSAKey *key, strbuf *outbuf);

+ 1 - 0
source/putty/ssh1bpp.c

@@ -236,6 +236,7 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
                        NULL, 0, NULL);
         }
 
+        s->pktin->qnode.formal_size = get_avail(s->pktin);
         pq_push(&s->bpp.in_pq, s->pktin);
 
         {

+ 5 - 2
source/putty/ssh1connection.c

@@ -43,6 +43,7 @@ static const struct PacketProtocolLayerVtable ssh1_connection_vtable = {
     ssh1_connection_want_user_input,
     ssh1_connection_got_user_input,
     ssh1_connection_reconfigure,
+    ssh_ppl_default_queued_data_size,
     NULL /* no layer names in SSH-1 */,
 };
 
@@ -520,10 +521,12 @@ static void ssh1_channel_close_local(struct ssh1_channel *c,
 {
     struct ssh1_connection_state *s = c->connlayer;
     PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
-    const char *msg = chan_log_close_msg(c->chan);
+    char *msg = chan_log_close_msg(c->chan);
 
-    if (msg != NULL)
+    if (msg != NULL) {
         ppl_logevent("%s%s%s", msg, reason ? " " : "", reason ? reason : "");
+        sfree(msg);
+    }
 
     chan_free(c->chan);
     c->chan = zombiechan_new();

+ 14 - 9
source/putty/ssh1login.c

@@ -86,6 +86,7 @@ static const struct PacketProtocolLayerVtable ssh1_login_vtable = {
     ssh1_login_want_user_input,
     ssh1_login_got_user_input,
     ssh1_login_reconfigure,
+    ssh_ppl_default_queued_data_size,
     NULL /* no layer names in SSH-1 */,
 };
 
@@ -427,7 +428,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
             ssh_user_close(s->ppl.ssh, "No username provided");
             return;
         }
-        s->username = dupstr(s->cur_prompt->prompts[0]->result);
+        s->username = prompt_get_result(s->cur_prompt->prompts[0]);
         free_prompts(s->cur_prompt);
         s->cur_prompt = NULL;
     }
@@ -702,7 +703,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                         ppl_printf("No passphrase required.\r\n");
                     passphrase = NULL;
                 } else {
-                    s->cur_prompt = new_prompts(s->ppl.seat);
+                    s->cur_prompt = new_prompts();
                     s->cur_prompt->to_server = false;
                     s->cur_prompt->from_server = false;
                     s->cur_prompt->name = dupstr("SSH key passphrase");
@@ -730,7 +731,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                                        "User aborted at passphrase prompt");
                         return;
                     }
-                    passphrase = dupstr(s->cur_prompt->prompts[0]->result);
+                    passphrase = prompt_get_result(s->cur_prompt->prompts[0]);
                     free_prompts(s->cur_prompt);
                     s->cur_prompt = NULL;
                 }
@@ -841,7 +842,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
         /*
          * Otherwise, try various forms of password-like authentication.
          */
-        s->cur_prompt = new_prompts(s->ppl.seat);
+        s->cur_prompt = new_prompts();
 
         if (conf_get_bool(s->conf, CONF_try_tis_auth) &&
             (s->supported_auths_mask & (1 << SSH1_AUTH_TIS)) &&
@@ -1042,8 +1043,10 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                  * we can use the primary defence.
                  */
                 int bottom, top, pwlen, i;
+                const char *pw = prompt_get_result_ref(
+                    s->cur_prompt->prompts[0]);
 
-                pwlen = strlen(s->cur_prompt->prompts[0]->result);
+                pwlen = strlen(pw);
                 if (pwlen < 16) {
                     bottom = 0;    /* zero length passwords are OK! :-) */
                     top = 15;
@@ -1057,7 +1060,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                 for (i = bottom; i <= top; i++) {
                     if (i == pwlen) {
                         pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type);
-                        put_stringz(pkt, s->cur_prompt->prompts[0]->result);
+                        put_stringz(pkt, pw);
                         pq_push(s->ppl.out_pq, pkt);
                     } else {
                         strbuf *random_data = strbuf_new_nm();
@@ -1080,7 +1083,8 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
 
                 ppl_logevent("Sending length-padded password");
                 pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type);
-                put_asciz(padded_pw, s->cur_prompt->prompts[0]->result);
+                put_asciz(padded_pw, prompt_get_result_ref(
+                              s->cur_prompt->prompts[0]));
                 size_t pad = 63 & -padded_pw->len;
                 random_read(strbuf_append(padded_pw, pad), pad);
                 put_stringsb(pkt, padded_pw);
@@ -1092,12 +1096,13 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                  */
                 ppl_logevent("Sending unpadded password");
                 pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type);
-                put_stringz(pkt, s->cur_prompt->prompts[0]->result);
+                put_stringz(pkt, prompt_get_result_ref(
+                                s->cur_prompt->prompts[0]));
                 pq_push(s->ppl.out_pq, pkt);
             }
         } else {
             pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type);
-            put_stringz(pkt, s->cur_prompt->prompts[0]->result);
+            put_stringz(pkt, prompt_get_result_ref(s->cur_prompt->prompts[0]));
             pq_push(s->ppl.out_pq, pkt);
         }
         ppl_logevent("Sent password");

+ 1 - 0
source/putty/ssh2bpp-bare.c

@@ -129,6 +129,7 @@ static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp)
             continue;
         }
 
+        s->pktin->qnode.formal_size = get_avail(s->pktin);
         pq_push(&s->bpp.in_pq, s->pktin);
         s->pktin = NULL;
     }

+ 1 - 0
source/putty/ssh2bpp.c

@@ -589,6 +589,7 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
             continue;
         }
 
+        s->pktin->qnode.formal_size = get_avail(s->pktin);
         pq_push(&s->bpp.in_pq, s->pktin);
 
         {

+ 5 - 1
source/putty/ssh2connection-client.c

@@ -315,7 +315,11 @@ SshChannel *ssh2_serverside_agent_open(ConnectionLayer *cl, Channel *chan)
 static void ssh2_channel_response(
     struct ssh2_channel *c, PktIn *pkt, void *ctx)
 {
-    chan_request_response(c->chan, pkt->type == SSH2_MSG_CHANNEL_SUCCESS);
+    /* If pkt==NULL (because this handler has been called in response
+     * to CHANNEL_CLOSE arriving while the request was still
+     * outstanding), we treat that the same as CHANNEL_FAILURE. */
+    chan_request_response(c->chan,
+                          pkt && pkt->type == SSH2_MSG_CHANNEL_SUCCESS);
 }
 
 void ssh2channel_start_shell(SshChannel *sc, bool want_reply)

+ 3 - 1
source/putty/ssh2connection.c

@@ -30,6 +30,7 @@ static const struct PacketProtocolLayerVtable ssh2_connection_vtable = {
     ssh2_connection_want_user_input,
     ssh2_connection_got_user_input,
     ssh2_connection_reconfigure,
+    ssh_ppl_default_queued_data_size,
     "ssh-connection",
 };
 
@@ -750,7 +751,8 @@ static bool ssh2_connection_filter_queue(struct ssh2_connection_state *s)
                         "Received %s for channel %d with no outstanding "
                         "channel request",
                         ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                      s->ppl.bpp->pls->actx, pktin->type));
+                                      s->ppl.bpp->pls->actx, pktin->type),
+                        c->localid);
                     return true;
                 }
                 ocr->handler(c, pktin, ocr->ctx);

+ 21 - 10
source/putty/ssh2transport.c

@@ -71,6 +71,7 @@ static void ssh2_transport_special_cmd(PacketProtocolLayer *ppl,
 static bool ssh2_transport_want_user_input(PacketProtocolLayer *ppl);
 static void ssh2_transport_got_user_input(PacketProtocolLayer *ppl);
 static void ssh2_transport_reconfigure(PacketProtocolLayer *ppl, Conf *conf);
+static size_t ssh2_transport_queued_data_size(PacketProtocolLayer *ppl);
 
 static void ssh2_transport_set_max_data_size(struct ssh2_transport_state *s);
 static unsigned long sanitise_rekey_time(int rekey_time, unsigned long def);
@@ -84,6 +85,7 @@ static const struct PacketProtocolLayerVtable ssh2_transport_vtable = {
     ssh2_transport_want_user_input,
     ssh2_transport_got_user_input,
     ssh2_transport_reconfigure,
+    ssh2_transport_queued_data_size,
     NULL, /* no protocol name for this layer */
 };
 
@@ -265,7 +267,7 @@ static void ssh2_mkkey(
      */
     keylen_padded = ((keylen + hlen - 1) / hlen) * hlen;
 
-    out->len = 0;
+    strbuf_clear(out);
     key = strbuf_append(out, keylen_padded);
 
     /* First hlen bytes. */
@@ -569,9 +571,10 @@ static void ssh2_write_kexinit_lists(
         }
     } else if (first_time) {
         /*
-         * In the first key exchange, we list all the algorithms
-         * we're prepared to cope with, but prefer those algorithms
-         * for which we have a host key for this host.
+         * In the first key exchange, we list all the algorithms we're
+         * prepared to cope with, but (if configured to) we prefer
+         * those algorithms for which we have a host key for this
+         * host.
          *
          * If the host key algorithm is below the warning
          * threshold, we warn even if we did already have a key
@@ -587,7 +590,8 @@ static void ssh2_write_kexinit_lists(
             for (j = 0; j < lenof(ssh2_hostkey_algs); j++) {
                 if (ssh2_hostkey_algs[j].id != preferred_hk[i])
                     continue;
-                if (have_ssh_host_key(hk_host, hk_port,
+                if (conf_get_bool(conf, CONF_ssh_prefer_known_hostkeys) &&
+                    have_ssh_host_key(hk_host, hk_port,
                                       ssh2_hostkey_algs[j].alg->cache_id)) {
                     alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY],
                                               ssh2_hostkey_algs[j].alg->ssh_id);
@@ -1083,7 +1087,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
      * Construct our KEXINIT packet, in a strbuf so we can refer to it
      * later.
      */
-    s->client_kexinit->len = 0;
+    strbuf_clear(s->client_kexinit);
     put_byte(s->outgoing_kexinit, SSH2_MSG_KEXINIT);
     random_read(strbuf_append(s->outgoing_kexinit, 16), 16);
     ssh2_write_kexinit_lists(
@@ -1121,7 +1125,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
                                       s->ppl.bpp->pls->actx, pktin->type));
         return;
     }
-    s->incoming_kexinit->len = 0;
+    strbuf_clear(s->incoming_kexinit);
     put_byte(s->incoming_kexinit, SSH2_MSG_KEXINIT);
     put_data(s->incoming_kexinit, get_ptr(pktin), get_avail(pktin));
 
@@ -1200,9 +1204,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
             if (better) {
                 if (betteralgs) {
                     char *old_ba = betteralgs;
-                    betteralgs = dupcat(betteralgs, ",",
-                                        hktype->alg->ssh_id,
-                                        (const char *)NULL);
+                    betteralgs = dupcat(betteralgs, ",", hktype->alg->ssh_id);
                     sfree(old_ba);
                 } else {
                     betteralgs = dupstr(hktype->alg->ssh_id);
@@ -2030,3 +2032,12 @@ static int ssh2_transport_confirm_weak_crypto_primitive(
     return seat_confirm_weak_crypto_primitive(
         s->ppl.seat, type, name, ssh2_transport_dialog_callback, s);
 }
+
+static size_t ssh2_transport_queued_data_size(PacketProtocolLayer *ppl)
+{
+    struct ssh2_transport_state *s =
+        container_of(ppl, struct ssh2_transport_state, ppl);
+
+    return (ssh_ppl_default_queued_data_size(ppl) +
+            ssh_ppl_queued_data_size(s->higher_layer));
+}

+ 27 - 16
source/putty/ssh2userauth.c

@@ -125,6 +125,7 @@ static const struct PacketProtocolLayerVtable ssh2_userauth_vtable = {
     ssh2_userauth_want_user_input,
     ssh2_userauth_got_user_input,
     ssh2_userauth_reconfigure,
+    ssh_ppl_default_queued_data_size,
     "ssh-userauth",
 };
 
@@ -469,7 +470,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
             }
             sfree(s->locally_allocated_username); /* for change_username */
             s->username = s->locally_allocated_username =
-                dupstr(s->cur_prompt->prompts[0]->result);
+                prompt_get_result(s->cur_prompt->prompts[0]);
             free_prompts(s->cur_prompt);
         } else {
             if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE))
@@ -536,9 +537,9 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                 while (bufchain_size(&s->banner) > 0) {
                     ptrlen data = bufchain_prefix(&s->banner);
                     seat_stderr_pl(s->ppl.seat, data);
-                    bufchain_consume(&s->banner, data.len);
                     mid_line =
                         (((const char *)data.ptr)[data.len-1] != '\n');
+                    bufchain_consume(&s->banner, data.len);
                 }
                 bufchain_clear(&s->banner);
 
@@ -647,7 +648,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                 /*
                  * Save the methods string for use in error messages.
                  */
-                s->last_methods_string->len = 0;
+                strbuf_clear(s->last_methods_string);
                 put_datapl(s->last_methods_string, methods);
 
                 /*
@@ -811,7 +812,15 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                             ppl_printf("Pageant failed to "
                                        "provide a signature\r\n");
                             s->suppress_wait_for_response_packet = true;
+                            ssh_free_pktout(s->pktout);
                         }
+                    } else {
+                        ppl_logevent("Pageant failed to respond to "
+                                     "signing request");
+                        ppl_printf("Pageant failed to "
+                                   "respond to signing request\r\n");
+                        s->suppress_wait_for_response_packet = true;
+                        ssh_free_pktout(s->pktout);
                     }
                 }
 
@@ -907,7 +916,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                             return;
                         }
                         passphrase =
-                            dupstr(s->cur_prompt->prompts[0]->result);
+                            prompt_get_result(s->cur_prompt->prompts[0]);
                         free_prompts(s->cur_prompt);
                     } else {
                         passphrase = NULL; /* no passphrase needed */
@@ -1357,6 +1366,8 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                     }
                     if (sb->len)
                         s->cur_prompt->instruction = strbuf_to_str(sb);
+                    else
+                        strbuf_free(sb);
 
                     /*
                      * Our prompts_t is fully constructed now. Get the
@@ -1397,8 +1408,8 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                         s->ppl.bpp, SSH2_MSG_USERAUTH_INFO_RESPONSE);
                     put_uint32(s->pktout, s->num_prompts);
                     for (uint32_t i = 0; i < s->num_prompts; i++) {
-                        put_stringz(s->pktout,
-                                    s->cur_prompt->prompts[i]->result);
+                        put_stringz(s->pktout, prompt_get_result_ref(
+                                        s->cur_prompt->prompts[i]));
                     }
                     s->pktout->minlen = 256;
                     pq_push(s->ppl.out_pq, s->pktout);
@@ -1480,7 +1491,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                  * Squirrel away the password. (We may need it later if
                  * asked to change it.)
                  */
-                s->password = dupstr(s->cur_prompt->prompts[0]->result);
+                s->password = prompt_get_result(s->cur_prompt->prompts[0]);
                 free_prompts(s->cur_prompt);
 
                 /*
@@ -1606,20 +1617,20 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                          * (A side effect is that the user doesn't have to
                          * re-enter it if they louse up the new password.)
                          */
-                        if (s->cur_prompt->prompts[0]->result[0]) {
+                        if (s->cur_prompt->prompts[0]->result->s[0]) {
                             smemclr(s->password, strlen(s->password));
                                 /* burn the evidence */
                             sfree(s->password);
-                            s->password =
-                                dupstr(s->cur_prompt->prompts[0]->result);
+                            s->password = prompt_get_result(
+                                s->cur_prompt->prompts[0]);
                         }
 
                         /*
                          * Check the two new passwords match.
                          */
-                        got_new = (strcmp(s->cur_prompt->prompts[1]->result,
-                                          s->cur_prompt->prompts[2]->result)
-                                   == 0);
+                        got_new = !strcmp(
+                            prompt_get_result_ref(s->cur_prompt->prompts[1]),
+                            prompt_get_result_ref(s->cur_prompt->prompts[2]));
                         if (!got_new)
                             /* They don't. Silly user. */
                             ppl_printf("Passwords do not match\r\n");
@@ -1637,8 +1648,8 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                     put_stringz(s->pktout, "password");
                     put_bool(s->pktout, true);
                     put_stringz(s->pktout, s->password);
-                    put_stringz(s->pktout,
-                                       s->cur_prompt->prompts[1]->result);
+                    put_stringz(s->pktout, prompt_get_result_ref(
+                                    s->cur_prompt->prompts[1]));
                     free_prompts(s->cur_prompt);
                     s->pktout->minlen = 256;
                     pq_push(s->ppl.out_pq, s->pktout);
@@ -1800,7 +1811,7 @@ static void ssh2_userauth_add_sigblob(
         /* debug("modulus length is %d\n", len); */
         /* debug("signature length is %d\n", siglen); */
 
-        if (mod_mp.len != sig_mp.len) {
+        if (mod_mp.len > sig_mp.len) {
             strbuf *substr = strbuf_new();
             put_data(substr, sigblob.ptr, sig_prefix_len);
             put_uint32(substr, mod_mp.len);

+ 36 - 4
source/putty/sshcommon.c

@@ -35,6 +35,7 @@ void pq_base_push(PacketQueueBase *pqb, PacketQueueNode *node)
     node->prev = pqb->end.prev;
     node->next->prev = node;
     node->prev->next = node;
+    pqb->total_size += node->formal_size;
 
     if (pqb->ic)
         queue_idempotent_callback(pqb->ic);
@@ -47,6 +48,7 @@ void pq_base_push_front(PacketQueueBase *pqb, PacketQueueNode *node)
     node->next = pqb->end.next;
     node->next->prev = node;
     node->prev->next = node;
+    pqb->total_size += node->formal_size;
 
     if (pqb->ic)
         queue_idempotent_callback(pqb->ic);
@@ -72,6 +74,23 @@ static IdempotentCallback ic_pktin_free = {
     pktin_free_queue_callback, NULL, false
 };
 
+static inline void pq_unlink_common(PacketQueueBase *pqb,
+                                    PacketQueueNode *node)
+{
+    node->next->prev = node->prev;
+    node->prev->next = node->next;
+
+    /* Check total_size doesn't drift out of sync downwards, by
+     * ensuring it doesn't underflow when we do this subtraction */
+    assert(pqb->total_size >= node->formal_size);
+    pqb->total_size -= node->formal_size;
+
+    /* Check total_size doesn't drift out of sync upwards, by checking
+     * that it's returned to exactly zero whenever a queue is
+     * emptied */
+    assert(pqb->end.next != &pqb->end || pqb->total_size == 0);
+}
+
 static PktIn *pq_in_after(PacketQueueBase *pqb,
                           PacketQueueNode *prev, bool pop)
 {
@@ -80,14 +99,14 @@ static PktIn *pq_in_after(PacketQueueBase *pqb,
         return NULL;
 
     if (pop) {
-        node->next->prev = node->prev;
-        node->prev->next = node->next;
+        pq_unlink_common(pqb, node);
 
         node->prev = pktin_freeq_head.prev;
         node->next = &pktin_freeq_head;
         node->next->prev = node;
         node->prev->next = node;
         node->on_free_queue = true;
+
         queue_idempotent_callback(&ic_pktin_free);
     }
 
@@ -102,8 +121,8 @@ static PktOut *pq_out_after(PacketQueueBase *pqb,
         return NULL;
 
     if (pop) {
-        node->next->prev = node->prev;
-        node->prev->next = node->next;
+        pq_unlink_common(pqb, node);
+
         node->prev = node->next = NULL;
     }
 
@@ -115,6 +134,7 @@ void pq_in_init(PktInQueue *pq)
     pq->pqb.ic = NULL;
     pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end;
     pq->after = pq_in_after;
+    pq->pqb.total_size = 0;
 }
 
 void pq_out_init(PktOutQueue *pq)
@@ -122,6 +142,7 @@ void pq_out_init(PktOutQueue *pq)
     pq->pqb.ic = NULL;
     pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end;
     pq->after = pq_out_after;
+    pq->pqb.total_size = 0;
 }
 
 void pq_in_clear(PktInQueue *pq)
@@ -153,6 +174,8 @@ void pq_base_concatenate(PacketQueueBase *qdest,
 {
     struct PacketQueueNode *head1, *tail1, *head2, *tail2;
 
+    size_t total_size = q1->total_size + q2->total_size;
+
     /*
      * Extract the contents from both input queues, and empty them.
      */
@@ -164,6 +187,7 @@ void pq_base_concatenate(PacketQueueBase *qdest,
 
     q1->end.next = q1->end.prev = &q1->end;
     q2->end.next = q2->end.prev = &q2->end;
+    q1->total_size = q2->total_size = 0;
 
     /*
      * Link the two lists together, handling the case where one or
@@ -206,6 +230,8 @@ void pq_base_concatenate(PacketQueueBase *qdest,
         if (qdest->ic)
             queue_idempotent_callback(qdest->ic);
     }
+
+    qdest->total_size = total_size;
 }
 
 /* ----------------------------------------------------------------------
@@ -235,6 +261,7 @@ static void ssh_pkt_adddata(PktOut *pkt, const void *data, int len)
     sgrowarrayn_nm(pkt->data, pkt->maxlen, pkt->length, len);
     memcpy(pkt->data + pkt->length, data, len);
     pkt->length += len;
+    pkt->qnode.formal_size = pkt->length;
 }
 
 static void ssh_pkt_BinarySink_write(BinarySink *bs,
@@ -808,6 +835,11 @@ void ssh_ppl_user_output_string_and_free(PacketProtocolLayer *ppl, char *text)
     sfree(text);
 }
 
+size_t ssh_ppl_default_queued_data_size(PacketProtocolLayer *ppl)
+{
+    return ppl->out_pq->pqb.total_size;
+}
+
 /* ----------------------------------------------------------------------
  * Common helper functions for clients and implementations of
  * BinaryPacketProtocol.

+ 3 - 1
source/putty/sshdss.c

@@ -72,8 +72,10 @@ static char *dss_cache_str(ssh_key *key)
     struct dss_key *dss = container_of(key, struct dss_key, sshk);
     strbuf *sb = strbuf_new();
 
-    if (!dss->p)
+    if (!dss->p) {
+        strbuf_free(sb);
         return NULL;
+    }
 
     append_hex_to_strbuf(sb, dss->p);
     append_hex_to_strbuf(sb, dss->q);

+ 1 - 1
source/putty/sshecc.c

@@ -1549,7 +1549,7 @@ bool ec_ed_alg_and_curve_by_bits(
     int bits, const struct ec_curve **curve, const ssh_keyalg **alg)
 {
     switch (bits) {
-      case 256: *alg = &ssh_ecdsa_ed25519; break;
+      case 255: case 256: *alg = &ssh_ecdsa_ed25519; break;
       default: return false;
     }
     *curve = ((struct ecsign_extra *)(*alg)->extra)->curve();

+ 1 - 1
source/putty/sshgssc.c

@@ -25,7 +25,7 @@ static Ssh_gss_stat ssh_gssapi_import_name(struct ssh_gss_library *lib,
     gss_buffer_desc host_buf;
     char *pStr;
 
-    pStr = dupcat("host@", host, NULL);
+    pStr = dupcat("host@", host);
 
     host_buf.value = pStr;
     host_buf.length = strlen(pStr);

+ 3 - 3
source/putty/sshhmac.c

@@ -44,7 +44,7 @@ static ssh2_mac *hmac_new(const ssh2_macalg *alg, ssh_cipher *cipher)
     ctx->digest = snewn(ctx->hashalg->hlen, uint8_t);
 
     ctx->text_name = strbuf_new();
-    strbuf_catf(ctx->text_name, "HMAC-%s",
+    strbuf_catf(ctx->text_name, "HMAC-%s%s",
                 ctx->hashalg->text_basename, extra->suffix);
     if (extra->annotation || ctx->hashalg->annotation) {
         strbuf_catf(ctx->text_name, " (");
@@ -222,7 +222,7 @@ const ssh2_macalg ssh_hmac_sha1_96 = {
 };
 
 const struct hmac_extra ssh_hmac_sha1_buggy_extra = {
-    &ssh_sha1, " (bug-compatible)"
+    &ssh_sha1, "", "bug-compatible"
 };
 
 const ssh2_macalg ssh_hmac_sha1_buggy = {
@@ -233,7 +233,7 @@ const ssh2_macalg ssh_hmac_sha1_buggy = {
 };
 
 const struct hmac_extra ssh_hmac_sha1_96_buggy_extra = {
-    &ssh_sha1, "-96 (bug-compatible)"
+    &ssh_sha1, "-96", "bug-compatible"
 };
 
 const ssh2_macalg ssh_hmac_sha1_96_buggy = {

+ 8 - 0
source/putty/sshppl.h

@@ -19,6 +19,7 @@ struct PacketProtocolLayerVtable {
     bool (*want_user_input)(PacketProtocolLayer *ppl);
     void (*got_user_input)(PacketProtocolLayer *ppl);
     void (*reconfigure)(PacketProtocolLayer *ppl, Conf *conf);
+    size_t (*queued_data_size)(PacketProtocolLayer *ppl);
 
     /* Protocol-level name of this layer. */
     const char *name;
@@ -73,6 +74,8 @@ static inline void ssh_ppl_got_user_input(PacketProtocolLayer *ppl)
 { ppl->vt->got_user_input(ppl); }
 static inline void ssh_ppl_reconfigure(PacketProtocolLayer *ppl, Conf *conf)
 { ppl->vt->reconfigure(ppl, conf); }
+static inline size_t ssh_ppl_queued_data_size(PacketProtocolLayer *ppl)
+{ return ppl->vt->queued_data_size(ppl); }
 
 /* ssh_ppl_free is more than just a macro wrapper on the vtable; it
  * does centralised parts of the freeing too. */
@@ -90,6 +93,11 @@ void ssh_ppl_setup_queues(PacketProtocolLayer *ppl,
  * avoid dereferencing itself on return from this function! */
 void ssh_ppl_replace(PacketProtocolLayer *old, PacketProtocolLayer *new);
 
+/* Default implementation of queued_data_size, which just adds up the
+ * sizes of all the packets in pq_out. A layer can override this if it
+ * has other things to take into account as well. */
+size_t ssh_ppl_default_queued_data_size(PacketProtocolLayer *ppl);
+
 PacketProtocolLayer *ssh1_login_new(
     Conf *conf, const char *host, int port,
     PacketProtocolLayer *successor_layer);

+ 4 - 4
source/putty/sshprng.c

@@ -177,7 +177,7 @@ static void prng_seed_BinarySink_write(
     prng *pr = BinarySink_DOWNCAST(bs, prng);
     prng_impl *pi = container_of(pr, prng_impl, Prng);
     assert(pi->keymaker);
-    prngdebug("prng: got %zu bytes of seed\n", len);
+    prngdebug("prng: got %"SIZEu" bytes of seed\n", len);
     put_data(pi->keymaker, data, len);
 }
 
@@ -228,7 +228,7 @@ void prng_read(prng *pr, void *vout, size_t size)
 
     assert(!pi->keymaker);
 
-    prngdebug("prng_read %zu\n", size);
+    prngdebug("prng_read %"SIZEu"\n", size);
 
     uint8_t *out = (uint8_t *)vout;
     for (; size > 0; size--) {
@@ -256,7 +256,7 @@ void prng_add_entropy(prng *pr, unsigned source_id, ptrlen data)
         index++;
     }
 
-    prngdebug("prng_add_entropy source=%u size=%zu -> collector %zi\n",
+    prngdebug("prng_add_entropy source=%u size=%"SIZEu" -> collector %zi\n",
               source_id, data.len, index);
 
     put_datapl(pi->collectors[index], data);
@@ -272,7 +272,7 @@ void prng_add_entropy(prng *pr, unsigned source_id, ptrlen data)
         uint32_t reseed_index = ++pi->reseeds;
         prngdebug("prng entropy reseed #%"PRIu32"\n", reseed_index);
         for (size_t i = 0; i < NCOLLECTORS; i++) {
-            prngdebug("emptying collector %zu\n", i);
+            prngdebug("emptying collector %"SIZEu"\n", i);
             ssh_hash_final(pi->collectors[i], pi->pending_output);
             put_data(&pi->Prng, pi->pending_output, pi->hashalg->hlen);
             pi->collectors[i] = ssh_hash_new(pi->hashalg);

+ 4 - 4
source/putty/sshpubk.c

@@ -400,8 +400,8 @@ bool rsa_ssh1_savekey(const Filename *filename, RSAKey *key,
 /*
  * PuTTY's own format for SSH-2 keys is as follows:
  *
- * The file is text. Lines are terminated by CRLF, although CR-only
- * and LF-only are tolerated on input.
+ * The file is text. Lines are terminated by LF by preference,
+ * although CRLF and CR-only are tolerated on input.
  *
  * The first line says "PuTTY-User-Key-File-2: " plus the name of the
  * algorithm ("ssh-dss", "ssh-rsa" etc).
@@ -1362,8 +1362,8 @@ char *ssh1_pubkey_str(RSAKey *key)
 
     dec1 = mp_get_decimal(key->exponent);
     dec2 = mp_get_decimal(key->modulus);
-    buffer = dupprintf("%zd %s %s%s%s", mp_get_nbits(key->modulus), dec1, dec2,
-                       key->comment ? " " : "",
+    buffer = dupprintf("%"SIZEu" %s %s%s%s", mp_get_nbits(key->modulus),
+                       dec1, dec2, key->comment ? " " : "",
                        key->comment ? key->comment : "");
     sfree(dec1);
     sfree(dec2);

+ 21 - 3
source/putty/sshrsa.c

@@ -43,6 +43,24 @@ void BinarySource_get_rsa_ssh1_priv(
     rsa->private_exponent = get_mp_ssh1(src);
 }
 
+RSAKey *BinarySource_get_rsa_ssh1_priv_agent(BinarySource *src)
+{
+    RSAKey *rsa = snew(RSAKey);
+    memset(rsa, 0, sizeof(RSAKey));
+
+    get_rsa_ssh1_pub(src, rsa, RSA_SSH1_MODULUS_FIRST);
+    get_rsa_ssh1_priv(src, rsa);
+
+    /* SSH-1 names p and q the other way round, i.e. we have the
+     * inverse of p mod q and not of q mod p. We swap the names,
+     * because our internal RSA wants iqmp. */
+    rsa->iqmp = get_mp_ssh1(src);
+    rsa->q = get_mp_ssh1(src);
+    rsa->p = get_mp_ssh1(src);
+
+    return rsa;
+}
+
 bool rsa_ssh1_encrypt(unsigned char *data, int length, RSAKey *key)
 {
     mp_int *b1, *b2;
@@ -278,7 +296,7 @@ char *rsa_ssh1_fingerprint(RSAKey *key)
     ssh_hash_final(hash, digest);
 
     out = strbuf_new();
-    strbuf_catf(out, "%d ", mp_get_nbits(key->modulus));
+    strbuf_catf(out, "%"SIZEu" ", mp_get_nbits(key->modulus));
     for (i = 0; i < 16; i++)
         strbuf_catf(out, "%s%02x", i ? ":" : "", digest[i]);
     if (key->comment)
@@ -761,7 +779,7 @@ char *rsa2_invalid(ssh_key *key, unsigned flags)
     const ssh_hashalg *halg = rsa2_hash_alg_for_flags(flags, &sign_alg_name);
     if (nbytes < rsa_pkcs1_length_of_fixed_parts(halg)) {
         return dupprintf(
-            "%zu-bit RSA key is too short to generate %s signatures",
+            "%"SIZEu"-bit RSA key is too short to generate %s signatures",
             bits, sign_alg_name);
     }
 
@@ -980,7 +998,7 @@ mp_int *ssh_rsakex_decrypt(
         if (out[i] == 1) {
             i++;  /* skip over the 1 byte */
             break;
-        } else if (out[i] != 1) {
+        } else if (out[i] != 0) {
             sfree(out);
             return NULL;
         }

+ 7 - 6
source/putty/sshshare.c

@@ -705,8 +705,8 @@ static void share_remove_forwarding(struct ssh_sharing_connstate *cs,
     sfree(fwd);
 }
 
-static void log_downstream(struct ssh_sharing_connstate *cs,
-                           const char *logfmt, ...)
+static PRINTF_LIKE(2, 3) void log_downstream(struct ssh_sharing_connstate *cs,
+                                             const char *logfmt, ...)
 {
     va_list ap;
     char *buf;
@@ -719,8 +719,8 @@ static void log_downstream(struct ssh_sharing_connstate *cs,
     sfree(buf);
 }
 
-static void log_general(struct ssh_sharing_state *sharestate,
-                        const char *logfmt, ...)
+static PRINTF_LIKE(2, 3) void log_general(struct ssh_sharing_state *sharestate,
+                                          const char *logfmt, ...)
 {
     va_list ap;
     char *buf;
@@ -1794,8 +1794,9 @@ static void share_receive(Plug *plug, int urgent, const char *data, size_t len)
     }
     if (cs->recvlen > 0 && cs->recvbuf[cs->recvlen-1] == '\015')
         cs->recvlen--;                 /* trim off \r before \n */
+    ptrlen verstring = make_ptrlen(cs->recvbuf, cs->recvlen);
     log_downstream(cs, "Downstream version string: %.*s",
-                   cs->recvlen, cs->recvbuf);
+                   PTRLEN_PRINTF(verstring));
     cs->got_verstring = true;
 
     /*
@@ -1858,7 +1859,7 @@ static void share_listen_closing(Plug *plug, const char *error_msg,
 static void share_send_verstring(ssh_sharing_connstate *cs)
 {
     char *fullstring = dupcat("[email protected]",
-                              cs->parent->server_verstring, "\015\012", NULL);
+                              cs->parent->server_verstring, "\015\012");
     sk_write(cs->sock, fullstring, strlen(fullstring));
     sfree(fullstring);
 

+ 1 - 2
source/putty/sshverstring.c

@@ -303,8 +303,7 @@ void ssh_verstring_handle_input(BinaryPacketProtocol *bpp)
     while (s->vstring->len > 0 &&
            (s->vstring->s[s->vstring->len-1] == '\r' ||
             s->vstring->s[s->vstring->len-1] == '\n'))
-        s->vstring->len--;
-    s->vstring->s[s->vstring->len] = '\0';
+        strbuf_shrink_by(s->vstring, 1);
 
     bpp_logevent("Remote version: %s", s->vstring->s);
 

+ 1 - 1
source/putty/tree234.c

@@ -1072,7 +1072,7 @@ int n_errors = 0;
 /*
  * Error reporting function.
  */
-void error(char *fmt, ...)
+PRINTF_LIKE(1, 2) void error(char *fmt, ...)
 {
     va_list ap;
     printf("ERROR: ");

+ 38 - 4
source/putty/utils.c

@@ -182,10 +182,21 @@ int main(void)
     printf("passed %d failed %d total %d\n", passes, fails, passes+fails);
     return fails != 0 ? 1 : 0;
 }
+
 /* Stubs to stop the rest of this module causing compile failures. */
-void modalfatalbox(const char *fmt, ...) {}
-int conf_get_int(Conf *conf, int primary) { return 0; }
-char *conf_get_str(Conf *conf, int primary) { return NULL; }
+static NORETURN void fatal_error(const char *p, ...)
+{
+    va_list ap;
+    fprintf(stderr, "host_string_test: ");
+    va_start(ap, p);
+    vfprintf(stderr, p, ap);
+    va_end(ap);
+    fputc('\n', stderr);
+    exit(1);
+}
+
+void out_of_memory(void) { fatal_error("out of memory"); }
+
 #endif /* TEST_HOST_STRFOO */
 
 /*
@@ -249,7 +260,7 @@ char *dupstr(const char *s)
 }
 
 /* Allocate the concatenation of N strings. Terminate arg list with NULL. */
-char *dupcat(const char *s1, ...)
+char *dupcat_fn(const char *s1, ...)
 {
     int len;
     char *p, *q, *sn;
@@ -418,6 +429,29 @@ void *strbuf_append(strbuf *buf_o, size_t len)
     return toret;
 }
 
+void strbuf_shrink_to(strbuf *buf, size_t new_len)
+{
+    assert(new_len <= buf->len);
+    buf->len = new_len;
+    buf->s[buf->len] = '\0';
+}
+
+void strbuf_shrink_by(strbuf *buf, size_t amount_to_remove)
+{
+    assert(amount_to_remove <= buf->len);
+    buf->len -= amount_to_remove;
+    buf->s[buf->len] = '\0';
+}
+
+bool strbuf_chomp(strbuf *buf, char char_to_remove)
+{
+    if (buf->len > 0 && buf->s[buf->len-1] == char_to_remove) {
+        strbuf_shrink_by(buf, 1);
+        return true;
+    }
+    return false;
+}
+
 static void strbuf_BinarySink_write(
     BinarySink *bs, const void *data, size_t len)
 {

+ 5 - 5
source/putty/version.h

@@ -1,6 +1,6 @@
 /* Generated by automated build script */
-#define RELEASE 0.73
-#define TEXTVER "Release 0.73"
-#define SSHVER "-Release-0.73"
-#define BINARY_VERSION 0,73,0,0
-#define SOURCE_COMMIT "745ed3ad3beaf52fc623827e770b3a068b238dd5"
+#define RELEASE 0.74
+#define TEXTVER "Release 0.74"
+#define SSHVER "-Release-0.74"
+#define BINARY_VERSION 0,74,0,0
+#define SOURCE_COMMIT "014d4fb151369f255b3debed7d15a154fd9036f5"

+ 1 - 1
source/putty/windows/wingss.c

@@ -308,7 +308,7 @@ static Ssh_gss_stat ssh_sspi_import_name(struct ssh_gss_library *lib,
     if (host == NULL) return SSH_GSS_FAILURE;
 
     /* copy it into form host/FQDN */
-    pStr = dupcat("host/", host, NULL);
+    pStr = dupcat("host/", host);
 
     *srv_name = (Ssh_gss_name) pStr;
 

+ 1 - 1
source/putty/windows/winmisc.c

@@ -233,7 +233,7 @@ HMODULE load_system32_dll(const char *libname)
             sgrowarray(sysdir, sysdirsize, len);
     }
 
-    fullpath = dupcat(sysdir, "\\", libname, NULL);
+    fullpath = dupcat(sysdir, "\\", libname);
     ret = LoadLibrary(fullpath);
     sfree(fullpath);
     return ret;

+ 1 - 2
source/putty/windows/winnet.c

@@ -1817,8 +1817,7 @@ char *get_hostname(void)
     return dupstr(hostbuf);
 }
 
-SockAddr *platform_get_x11_unix_address(const char *display, int displaynum,
-                                       char **canonicalname)
+SockAddr *platform_get_x11_unix_address(const char *display, int displaynum)
 {
     SockAddr *ret = snew(SockAddr);
     memset(ret, 0, sizeof(SockAddr));

+ 10 - 14
source/putty/windows/winstore.c

@@ -175,7 +175,7 @@ FontSpec *read_setting_fontspec(settings_r *handle, const char *name)
     if (!fontname)
         return NULL;
 
-    settingname = dupcat(name, "IsBold", NULL);
+    settingname = dupcat(name, "IsBold");
     isbold = read_setting_i(handle, settingname, -1);
     sfree(settingname);
     if (isbold == -1) {
@@ -183,7 +183,7 @@ FontSpec *read_setting_fontspec(settings_r *handle, const char *name)
         return NULL;
     }
 
-    settingname = dupcat(name, "CharSet", NULL);
+    settingname = dupcat(name, "CharSet");
     charset = read_setting_i(handle, settingname, -1);
     sfree(settingname);
     if (charset == -1) {
@@ -191,7 +191,7 @@ FontSpec *read_setting_fontspec(settings_r *handle, const char *name)
         return NULL;
     }
 
-    settingname = dupcat(name, "Height", NULL);
+    settingname = dupcat(name, "Height");
     height = read_setting_i(handle, settingname, INT_MIN);
     sfree(settingname);
     if (height == INT_MIN) {
@@ -210,13 +210,13 @@ void write_setting_fontspec(settings_w *handle,
     char *settingname;
 
     write_setting_s(handle, name, font->name);
-    settingname = dupcat(name, "IsBold", NULL);
+    settingname = dupcat(name, "IsBold");
     write_setting_i(handle, settingname, font->isbold);
     sfree(settingname);
-    settingname = dupcat(name, "CharSet", NULL);
+    settingname = dupcat(name, "CharSet");
     write_setting_i(handle, settingname, font->charset);
     sfree(settingname);
-    settingname = dupcat(name, "Height", NULL);
+    settingname = dupcat(name, "Height");
     write_setting_i(handle, settingname, font->height);
     sfree(settingname);
 }
@@ -551,15 +551,13 @@ static HANDLE access_random_seed(int action)
         char profile[MAX_PATH + 1];
         if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA,
                                          NULL, SHGFP_TYPE_CURRENT, profile)) &&
-            try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND",
-                                            (const char *)NULL),
+            try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND"),
                                      action, &rethandle))
             return rethandle;
 
         if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_APPDATA,
                                          NULL, SHGFP_TYPE_CURRENT, profile)) &&
-            try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND",
-                                            (const char *)NULL),
+            try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND"),
                                      action, &rethandle))
             return rethandle;
     }
@@ -582,8 +580,7 @@ static HANDLE access_random_seed(int action)
 
         if (drvlen < lenof(drv) && pathlen < lenof(path) && pathlen > 0 &&
             try_random_seed_and_free(
-                dupcat(drv, path, "\\PUTTY.RND", (const char *)NULL),
-                action, &rethandle))
+                dupcat(drv, path, "\\PUTTY.RND"), action, &rethandle))
             return rethandle;
     }
 
@@ -595,8 +592,7 @@ static HANDLE access_random_seed(int action)
         DWORD len = GetWindowsDirectory(windir, sizeof(windir));
         if (len < lenof(windir) &&
             try_random_seed_and_free(
-                dupcat(windir, "\\PUTTY.RND", (const char *)NULL),
-                action, &rethandle))
+                dupcat(windir, "\\PUTTY.RND"), action, &rethandle))
             return rethandle;
     }
 

+ 1 - 1
source/putty/windows/winstuff.h

@@ -350,7 +350,7 @@ DECL_WINDOWS_FUNCTION(GLOBAL, int, select,
  * Provided by each client of winnet.c, and called by winnet.c to turn
  * on or off WSA*Select for a given socket.
  */
-char *do_select(SOCKET skt, bool startup);
+char *do_select(SOCKET skt, bool enable);
 
 /*
  * Network-subsystem-related functions provided in other Windows modules.