Browse Source

Merge branch 'thirdparty_dev' into dev

Plus changes to make the code compilable on Embarcadero compiler

# Conflicts:
#	source/putty/WINDOWS/winnet.c
#	source/putty/WINDOWS/winpgntc.c

#	source/putty/mainchan.c
#	source/putty/misc.h
#	source/putty/network.h
#	source/putty/ssh.c
#	source/putty/ssh.h
#	source/putty/ssh1connection.c
#	source/putty/ssh2transport.c
#	source/putty/sshchan.h
#	source/putty/sshecc.c
#	source/putty/sshrand.c

Source commit: 6ac71c835baaf38587b4384ad16e3909c34aa85d
Martin Prikryl 6 years ago
parent
commit
63a489c5af
47 changed files with 673 additions and 280 deletions
  1. 3 1
      source/putty/defs.h
  2. 2 2
      source/putty/import.c
  3. 2 22
      source/putty/mainchan.c
  4. 27 12
      source/putty/memory.c
  5. 40 11
      source/putty/misc.c
  6. 52 34
      source/putty/misc.h
  7. 4 4
      source/putty/network.h
  8. 2 0
      source/putty/portfwd.c
  9. 11 14
      source/putty/putty.h
  10. 5 5
      source/putty/puttymem.h
  11. 42 5
      source/putty/ssh.c
  12. 29 8
      source/putty/ssh.h
  13. 3 2
      source/putty/ssh1bpp.c
  14. 5 0
      source/putty/ssh1connection-client.c
  15. 41 0
      source/putty/ssh1connection.c
  16. 7 0
      source/putty/ssh1connection.h
  17. 14 11
      source/putty/ssh1login.c
  18. 1 0
      source/putty/ssh2bpp-bare.c
  19. 1 0
      source/putty/ssh2bpp.c
  20. 21 0
      source/putty/ssh2connection.c
  21. 1 0
      source/putty/ssh2connection.h
  22. 2 3
      source/putty/ssh2kex-client.c
  23. 1 0
      source/putty/ssh2transhk.c
  24. 45 14
      source/putty/ssh2transport.c
  25. 4 6
      source/putty/ssh2transport.h
  26. 41 9
      source/putty/ssh2userauth.c
  27. 9 7
      source/putty/sshaes.c
  28. 1 0
      source/putty/sshbpp.h
  29. 2 2
      source/putty/sshchan.h
  30. 25 6
      source/putty/sshecc.c
  31. 2 2
      source/putty/sshppl.h
  32. 2 2
      source/putty/sshpubk.c
  33. 25 5
      source/putty/sshrand.c
  34. 5 2
      source/putty/sshrsa.c
  35. 0 2
      source/putty/sshsha.c
  36. 1 0
      source/putty/sshverstring.c
  37. 50 3
      source/putty/sshzlib.c
  38. 13 6
      source/putty/stripctrl.c
  39. 40 1
      source/putty/utils.c
  40. 5 5
      source/putty/version.h
  41. 4 0
      source/putty/windows/wingss.c
  42. 2 0
      source/putty/windows/winhsock.c
  43. 3 6
      source/putty/windows/winnet.c
  44. 0 12
      source/putty/windows/winnoise.c
  45. 5 5
      source/putty/windows/winpgntc.c
  46. 55 40
      source/putty/windows/winstore.c
  47. 18 11
      source/putty/windows/winucs.c

+ 3 - 1
source/putty/defs.h

@@ -18,12 +18,13 @@
 #ifndef WINSCP
 // This is used in pageant, pscp, psftp and servers only
 #if defined _MSC_VER && _MSC_VER < 1800
-/* Work around lack of inttypes.h in older MSVC */
+/* Work around lack of inttypes.h and strtoumax in older MSVC */
 #define PRIx32 "x"
 #define PRIu64 "I64u"
 #define PRIdMAX "I64d"
 #define PRIXMAX "I64X"
 #define SCNu64 "I64u"
+uintmax_t strtoumax(const char *nptr, char **endptr, int base);
 #else
 #include <inttypes.h>
 #endif
@@ -82,6 +83,7 @@ typedef struct MontgomeryPoint MontgomeryPoint;
 typedef struct EdwardsCurve EdwardsCurve;
 typedef struct EdwardsPoint EdwardsPoint;
 
+typedef struct SshServerConfig SshServerConfig;
 typedef struct SftpServer SftpServer;
 typedef struct SftpServerVtable SftpServerVtable;
 

+ 2 - 2
source/putty/import.c

@@ -1168,7 +1168,7 @@ static struct openssh_new_key *load_openssh_new_key(const Filename *filename,
     fclose(fp);
     fp = NULL;
 
-    if (ret->keyblob->len == 0 || !ret->keyblob) {
+    if (ret->keyblob->len == 0) {
 	errmsg = "key body not present";
 	goto error;
     }
@@ -1444,7 +1444,7 @@ static ssh2_userkey *openssh_new_read(
         }
     }
 
-    if (!retkey) {
+    if (!retkey->key) {
         errmsg = "key index out of range";
         goto error;
     }

+ 2 - 22
source/putty/mainchan.c

@@ -334,33 +334,13 @@ static void mainchan_ready(mainchan *mc)
     queue_idempotent_callback(&mc->ppl->ic_process_queue);
 }
 
-struct mainchan_open_failure_abort_ctx {
-    Ssh *ssh;
-    char *abort_message;
-};
-
-static void mainchan_open_failure_abort(void *vctx)
-{
-    struct mainchan_open_failure_abort_ctx *ctx =
-        (struct mainchan_open_failure_abort_ctx *)vctx;
-    ssh_sw_abort(
-        ctx->ssh, "Server refused to open main channel: %s",
-        ctx->abort_message);
-    sfree(ctx->abort_message);
-    sfree(ctx);
-}
-
 static void mainchan_open_failure(Channel *chan, const char *errtext)
 {
     pinitassert(chan->vt == &mainchan_channelvt);
     mainchan *mc = container_of(chan, mainchan, chan);
 
-    struct mainchan_open_failure_abort_ctx *ctx =
-        snew(struct mainchan_open_failure_abort_ctx);
-
-    ctx->ssh = mc->ppl->ssh;
-    ctx->abort_message = dupstr(errtext);
-    queue_toplevel_callback(get_log_callback_set(mc->cl->logctx), mainchan_open_failure_abort, ctx);
+    ssh_sw_abort_deferred(mc->ppl->ssh,
+                          "Server refused to open main channel: %s", errtext);
 }
 
 static size_t mainchan_send(Channel *chan, bool is_stderr,

+ 27 - 12
source/putty/memory.c

@@ -10,26 +10,41 @@
 #include "puttymem.h"
 #include "misc.h"
 
-void *safemalloc(size_t n, size_t size)
+void *safemalloc(size_t factor1, size_t factor2, size_t addend)
 {
-    void *p;
+    if (factor1 > SIZE_MAX / factor2)
+        goto fail;
+    { // WINSCP
+    size_t product = factor1 * factor2;
 
-    if (n > INT_MAX / size) {
-	p = NULL;
-    } else {
-	size *= n;
-	if (size == 0) size = 1;
+    if (addend > SIZE_MAX)
+        goto fail;
+    if (product > SIZE_MAX - addend)
+        goto fail;
+    { // WINSCP
+    size_t size = product + addend;
+
+    if (size == 0)
+        size = 1;
+
+    { // WINSCP
+    void *p;
 #ifdef MINEFIELD
-	p = minefield_c_malloc(size);
+    p = minefield_c_malloc(size);
 #else
-	p = malloc(size);
+    p = malloc(size);
 #endif
-    }
 
     if (!p)
-        out_of_memory();
+        goto fail;
 
     return p;
+
+  fail:
+    out_of_memory();
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
 }
 
 void *saferealloc(void *ptr, size_t n, size_t size)
@@ -114,7 +129,7 @@ void *safegrowarray(void *ptr, size_t *allocated, size_t eltsize,
     size_t newsize = oldsize + increment;
     void *toret;
     if (secret) {
-        toret = safemalloc(newsize, eltsize);
+        toret = safemalloc(newsize, eltsize, 0);
         memcpy(toret, ptr, oldsize * eltsize);
         smemclr(ptr, oldsize * eltsize);
         sfree(ptr);

+ 40 - 11
source/putty/misc.c

@@ -239,28 +239,57 @@ char *buildinfo(const char *newline)
     strbuf_catf(buf, ", emulating ");
 #endif
     strbuf_catf(buf, "Visual Studio", newline);
-#if _MSC_VER == 1900
-    strbuf_catf(buf, " 2015 / MSVC++ 14.0");
+
+#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 == 1920
+    strbuf_catf(buf, " 2019 (16.x)");
+#elif _MSC_VER == 1916
+    strbuf_catf(buf, " 2017 version 15.9");
+#elif _MSC_VER == 1915
+    strbuf_catf(buf, " 2017 version 15.8");
+#elif _MSC_VER == 1914
+    strbuf_catf(buf, " 2017 version 15.7");
+#elif _MSC_VER == 1913
+    strbuf_catf(buf, " 2017 version 15.6");
 #elif _MSC_VER == 1912
-    strbuf_catf(buf, " 2017 / MSVC++ 14.12");
+    strbuf_catf(buf, " 2017 version 15.5");
+#elif _MSC_VER == 1911
+    strbuf_catf(buf, " 2017 version 15.3");
+#elif _MSC_VER == 1910
+    strbuf_catf(buf, " 2017 RTW (15.0)");
+#elif _MSC_VER == 1900
+    strbuf_catf(buf, " 2015 (14.0)");
 #elif _MSC_VER == 1800
-    strbuf_catf(buf, " 2013 / MSVC++ 12.0");
+    strbuf_catf(buf, " 2013 (12.0)");
 #elif _MSC_VER == 1700
-    strbuf_catf(buf, " 2012 / MSVC++ 11.0");
+    strbuf_catf(buf, " 2012 (11.0)");
 #elif _MSC_VER == 1600
-    strbuf_catf(buf, " 2010 / MSVC++ 10.0");
+    strbuf_catf(buf, " 2010 (10.0)");
 #elif _MSC_VER == 1500
-    strbuf_catf(buf, " 2008 / MSVC++ 9.0");
+    strbuf_catf(buf, " 2008 (9.0)");
 #elif _MSC_VER == 1400
-    strbuf_catf(buf, " 2005 / MSVC++ 8.0");
+    strbuf_catf(buf, " 2005 (8.0)");
 #elif _MSC_VER == 1310
-    strbuf_catf(buf, " 2003 / MSVC++ 7.1");
+    strbuf_catf(buf, " .NET 2003 (7.1)");
 #elif _MSC_VER == 1300
-    strbuf_catf(buf, " 2003 / MSVC++ 7.0");
+    strbuf_catf(buf, " .NET 2002 (7.0)");
+#elif _MSC_VER == 1200
+    strbuf_catf(buf, " 6.0");
 #else
     strbuf_catf(buf, ", unrecognised version");
 #endif
-    strbuf_catf(buf, " (_MSC_VER=%d)", (int)_MSC_VER);
+    strbuf_catf(buf, ", _MSC_VER=%d", (int)_MSC_VER);
 #endif
 
 #ifdef BUILDINFO_GTK

+ 52 - 34
source/putty/misc.h

@@ -24,11 +24,27 @@ 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 (printf, 1, 2)))
+    __attribute__ ((format (PUTTY_PRINTF_ARCHETYPE, 1, 2)))
 #endif
     ;
 char *dupvprintf(const char *fmt, va_list ap);
@@ -97,6 +113,7 @@ static inline int toint(unsigned u)
 }
 
 char *fgetline(FILE *fp);
+bool read_file_into(BinarySink *bs, FILE *fp);
 char *chomp(char *str);
 bool strstartswith(const char *s, const char *t);
 bool strendswith(const char *s, const char *t);
@@ -168,8 +185,14 @@ static inline ptrlen ptrlen_from_strbuf(strbuf *sb)
 bool ptrlen_eq_string(ptrlen pl, const char *str);
 bool ptrlen_eq_ptrlen(ptrlen pl1, ptrlen pl2);
 int ptrlen_strcmp(ptrlen pl1, ptrlen pl2);
+/* ptrlen_startswith and ptrlen_endswith write through their 'tail'
+ * argument if and only if it is non-NULL and they return true. Hence
+ * you can write ptrlen_startswith(thing, prefix, &thing), writing
+ * back to the same ptrlen it read from, to remove a prefix if present
+ * and say whether it did so. */
 bool ptrlen_startswith(ptrlen whole, ptrlen prefix, ptrlen *tail);
 bool ptrlen_endswith(ptrlen whole, ptrlen suffix, ptrlen *tail);
+ptrlen ptrlen_get_word(ptrlen *input, const char *separators);
 char *mkstr(ptrlen pl);
 int string_length_for_printf(size_t);
 /* Derive two printf arguments from a ptrlen, suitable for "%.*s" */
@@ -274,15 +297,14 @@ static inline uint64_t GET_64BIT_LSB_FIRST(const void *vp)
 static inline void PUT_64BIT_LSB_FIRST(void *vp, uint64_t value)
 {
     uint8_t *p = (uint8_t *)vp;
-    // WINSCP cast to uint8_t
-    p[0] = (uint8_t)value;
-    p[1] = (uint8_t)((value) >> 8);
-    p[2] = (uint8_t)((value) >> 16);
-    p[3] = (uint8_t)((value) >> 24);
-    p[4] = (uint8_t)((value) >> 32);
-    p[5] = (uint8_t)((value) >> 40);
-    p[6] = (uint8_t)((value) >> 48);
-    p[7] = (uint8_t)((value) >> 56);
+    p[0] = (uint8_t)(value);
+    p[1] = (uint8_t)(value >> 8);
+    p[2] = (uint8_t)(value >> 16);
+    p[3] = (uint8_t)(value >> 24);
+    p[4] = (uint8_t)(value >> 32);
+    p[5] = (uint8_t)(value >> 40);
+    p[6] = (uint8_t)(value >> 48);
+    p[7] = (uint8_t)(value >> 56);
 }
 
 static inline uint32_t GET_32BIT_LSB_FIRST(const void *vp)
@@ -295,11 +317,10 @@ static inline uint32_t GET_32BIT_LSB_FIRST(const void *vp)
 static inline void PUT_32BIT_LSB_FIRST(void *vp, uint32_t value)
 {
     uint8_t *p = (uint8_t *)vp;
-    // WINSCP cast to uint8_t
-    p[0] = (uint8_t)value;
-    p[1] = (uint8_t)((value) >> 8);
-    p[2] = (uint8_t)((value) >> 16);
-    p[3] = (uint8_t)((value) >> 24);
+    p[0] = (uint8_t)(value);
+    p[1] = (uint8_t)(value >> 8);
+    p[2] = (uint8_t)(value >> 16);
+    p[3] = (uint8_t)(value >> 24);
 }
 
 static inline uint16_t GET_16BIT_LSB_FIRST(const void *vp)
@@ -311,9 +332,8 @@ static inline uint16_t GET_16BIT_LSB_FIRST(const void *vp)
 static inline void PUT_16BIT_LSB_FIRST(void *vp, uint16_t value)
 {
     uint8_t *p = (uint8_t *)vp;
-    // WINSCP cast to uint8_t
-    p[0] = (uint8_t)value;
-    p[1] = (uint8_t)((value) >> 8);
+    p[0] = (uint8_t)(value);
+    p[1] = (uint8_t)(value >> 8);
 }
 
 static inline uint64_t GET_64BIT_MSB_FIRST(const void *vp)
@@ -328,15 +348,14 @@ static inline uint64_t GET_64BIT_MSB_FIRST(const void *vp)
 static inline void PUT_64BIT_MSB_FIRST(void *vp, uint64_t value)
 {
     uint8_t *p = (uint8_t *)vp;
-    // WINSCP cast to uint8_t
-    p[7] = (uint8_t)value;
-    p[6] = (uint8_t)((value) >> 8);
-    p[5] = (uint8_t)((value) >> 16);
-    p[4] = (uint8_t)((value) >> 24);
-    p[3] = (uint8_t)((value) >> 32);
-    p[2] = (uint8_t)((value) >> 40);
-    p[1] = (uint8_t)((value) >> 48);
-    p[0] = (uint8_t)((value) >> 56);
+    p[7] = (uint8_t)(value);
+    p[6] = (uint8_t)(value >> 8);
+    p[5] = (uint8_t)(value >> 16);
+    p[4] = (uint8_t)(value >> 24);
+    p[3] = (uint8_t)(value >> 32);
+    p[2] = (uint8_t)(value >> 40);
+    p[1] = (uint8_t)(value >> 48);
+    p[0] = (uint8_t)(value >> 56);
 }
 
 static inline uint32_t GET_32BIT_MSB_FIRST(const void *vp)
@@ -349,11 +368,10 @@ static inline uint32_t GET_32BIT_MSB_FIRST(const void *vp)
 static inline void PUT_32BIT_MSB_FIRST(void *vp, uint32_t value)
 {
     uint8_t *p = (uint8_t *)vp;
-    // WINSCP cast to uint8_t
-    p[3] = (uint8_t)value;
-    p[2] = (uint8_t)((value) >> 8);
-    p[1] = (uint8_t)((value) >> 16);
-    p[0] = (uint8_t)((value) >> 24);
+    p[3] = (uint8_t)(value);
+    p[2] = (uint8_t)(value >> 8);
+    p[1] = (uint8_t)(value >> 16);
+    p[0] = (uint8_t)(value >> 24);
 }
 
 static inline uint16_t GET_16BIT_MSB_FIRST(const void *vp)
@@ -365,8 +383,8 @@ static inline uint16_t GET_16BIT_MSB_FIRST(const void *vp)
 static inline void PUT_16BIT_MSB_FIRST(void *vp, uint16_t value)
 {
     uint8_t *p = (uint8_t *)vp;
-    p[1] = (uint8_t)value;
-    p[0] = (value) >> 8;
+    p[1] = (uint8_t)(value);
+    p[0] = (uint8_t)(value >> 8);
 }
 
 /* Replace NULL with the empty string, permitting an idiom in which we

+ 4 - 4
source/putty/network.h

@@ -173,14 +173,14 @@ static inline void sk_flush(Socket *s)
 
 static inline void plug_log(
     Plug *p, int type, SockAddr *addr, int port, const char *msg, int code)
-{ /*WINSCP return*/ p->vt->log(p, type, addr, port, msg, code); }
+{ p->vt->log(p, type, addr, port, msg, code); }
 static inline void plug_closing(
     Plug *p, const char *msg, int code, bool calling_back)
-{ /*WINSCP return*/ p->vt->closing(p, msg, code, calling_back); }
+{ p->vt->closing(p, msg, code, calling_back); }
 static inline void plug_receive(Plug *p, int urg, const char *data, size_t len)
-{ /*WINSCP return*/ p->vt->receive(p, urg, data, len); }
+{ p->vt->receive(p, urg, data, len); }
 static inline void plug_sent (Plug *p, size_t bufsize)
-{ /*WINSCP return*/ p->vt->sent(p, bufsize); }
+{ p->vt->sent(p, bufsize); }
 static inline int plug_accepting(Plug *p, accept_fn_t cons, accept_ctx_t ctx)
 { return p->vt->accepting(p, cons, ctx); }
 

+ 2 - 0
source/putty/portfwd.c

@@ -961,8 +961,10 @@ void portfwdmgr_config(PortFwdManager *mgr, Conf *conf)
                  * rejected.
                  */
                 ssh_rportfwd_remove(mgr->cl, pfr->remote);
+                pfr->remote = NULL;
             } else if (pfr->local) {
                 pfl_terminate(pfr->local);
+                pfr->local = NULL;
             }
 
             delpos234(mgr->forwardings, i);

+ 11 - 14
source/putty/putty.h

@@ -1393,8 +1393,8 @@ NORETURN void cleanup_exit(int);
     X(INT, NONE, window_border) /* in pixels */ \
     X(STR, NONE, answerback) \
     X(STR, NONE, printer) \
-    X(BOOL, NONE, arabicshaping) \
-    X(BOOL, NONE, bidi) \
+    X(BOOL, NONE, no_arabicshaping) \
+    X(BOOL, NONE, no_bidi) \
     /* Colour options */ \
     X(BOOL, NONE, ansi_colour) \
     X(BOOL, NONE, xterm_256_colour) \
@@ -1539,10 +1539,6 @@ bool conf_deserialise(Conf *conf, BinarySource *src);/*returns true on success*/
  * Functions to copy, free, serialise and deserialise FontSpecs.
  * Provided per-platform, to go with the platform's idea of a
  * FontSpec's contents.
- *
- * fontspec_serialise returns the number of bytes written, and can
- * handle data==NULL without crashing. So you can call it once to find
- * out a size, then again once you've allocated a buffer.
  */
 FontSpec *fontspec_copy(const FontSpec *f);
 void fontspec_free(FontSpec *f);
@@ -1640,7 +1636,6 @@ void term_invalidate(Terminal *);
 void term_blink(Terminal *, bool set_cursor);
 void term_do_paste(Terminal *, const wchar_t *, int);
 void term_nopaste(Terminal *);
-bool term_ldisc(Terminal *, int option);
 void term_copyall(Terminal *, const int *, int);
 void term_reconfig(Terminal *, Conf *);
 void term_request_copy(Terminal *, const int *clipboards, int n_clipboards);
@@ -1653,6 +1648,8 @@ void term_set_focus(Terminal *term, bool has_focus);
 char *term_get_ttymode(Terminal *term, const char *mode);
 int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input);
 void term_set_trust_status(Terminal *term, bool trusted);
+void term_keyinput(Terminal *, int codepage, const void *buf, int len);
+void term_keyinputw(Terminal *, const wchar_t * widebuf, int len);
 
 typedef enum SmallKeypadKey {
     SKK_HOME, SKK_END, SKK_INSERT, SKK_DELETE, SKK_PGUP, SKK_PGDN,
@@ -1789,13 +1786,6 @@ void ldisc_free(Ldisc *);
 void ldisc_send(Ldisc *, const void *buf, int len, bool interactive);
 void ldisc_echoedit_update(Ldisc *);
 
-/*
- * Exports from ldiscucs.c.
- */
-void lpage_send(Ldisc *, int codepage, const char *buf, int len,
-                bool interactive);
-void luni_send(Ldisc *, const wchar_t * widebuf, int len, bool interactive);
-
 /*
  * Exports from sshrand.c.
  */
@@ -1809,6 +1799,13 @@ extern int random_active;
  * calls random_ref on startup and random_unref on shutdown. */
 void random_ref(void);
 void random_unref(void);
+/* random_clear is equivalent to calling random_unref as many times as
+ * necessary to shut down the global PRNG instance completely. It's
+ * not needed in normal applications, but the command-line PuTTYgen
+ * test finds it useful to clean up after each invocation of the
+ * logical main() no matter whether it needed random numbers or
+ * not. */
+void random_clear(void);
 /* random_setup_special is used by PuTTYgen. It makes an extra-big
  * random number generator. */
 void random_setup_special();

+ 5 - 5
source/putty/puttymem.h

@@ -10,13 +10,13 @@
 
 #include "defs.h"
 
-#define smalloc(z) safemalloc(z,1)
+#define smalloc(z) safemalloc(z,1,0)
 #define snmalloc safemalloc
 #define srealloc(y,z) saferealloc(y,z,1)
 #define snrealloc saferealloc
 #define sfree safefree
 
-void *safemalloc(size_t, size_t);
+void *safemalloc(size_t factor1, size_t factor2, size_t addend);
 void *saferealloc(void *, size_t, size_t);
 void safefree(void *);
 
@@ -28,8 +28,8 @@ void safefree(void *);
  * TYPECHECK to verify that the _input_ pointer is a pointer to the
  * correct type.
  */
-#define snew(type) ((type *)snmalloc(1, sizeof(type)))
-#define snewn(n, type) ((type *)snmalloc((n), sizeof(type)))
+#define snew(type) ((type *)snmalloc(1, sizeof(type), 0))
+#define snewn(n, type) ((type *)snmalloc((n), sizeof(type), 0))
 #define sresize(ptr, n, type) TYPECHECK((type *)0 == (ptr), \
     ((type *)snrealloc((ptr), (n), sizeof(type))))
 
@@ -45,7 +45,7 @@ void safefree(void *);
  * result to void *, so you can assign it straight to wherever you
  * wanted it.
  */
-#define snew_plus(type, extra) ((type *)snmalloc(1, sizeof(type) + (extra)))
+#define snew_plus(type, extra) ((type *)snmalloc(1, sizeof(type), (extra)))
 #define snew_plus_get_aux(ptr) ((void *)((ptr) + 1))
 
 /*

+ 42 - 5
source/putty/ssh.c

@@ -50,7 +50,9 @@ struct Ssh {
     ssh_sharing_state *connshare;
     bool attempting_connshare;
 
+#ifndef NO_GSSAPI
     struct ssh_connection_shared_gss_state gss_state;
+#endif
 
     char *savedhost;
     int savedport;
@@ -131,6 +133,8 @@ struct Ssh {
 
     Pinger *pinger;
 
+    char *deferred_abort_message;
+
     bool need_random_unref;
 };
 
@@ -249,15 +253,24 @@ static void ssh_got_ssh_version(struct ssh_version_receiver *rcv,
                 userauth_layer = ssh2_userauth_new(
                     connection_layer, ssh->savedhost, ssh->fullhostname,
                     conf_get_filename(ssh->conf, CONF_keyfile),
+                    conf_get_bool(ssh->conf, CONF_ssh_show_banner),
                     conf_get_bool(ssh->conf, CONF_tryagent), username,
                     conf_get_bool(ssh->conf, CONF_change_username),
                     conf_get_bool(ssh->conf, CONF_try_ki_auth),
+#ifndef NO_GSSAPI
                     conf_get_bool(ssh->conf, CONF_try_gssapi_auth),
                     conf_get_bool(ssh->conf, CONF_try_gssapi_kex),
                     conf_get_bool(ssh->conf, CONF_gssapifwd),
-                    &ssh->gss_state,
-                    conf_get_str(ssh->conf, CONF_loghost),
-                    conf_get_bool(ssh->conf, CONF_change_password)); // WINSCP
+                    &ssh->gss_state
+#else
+                    false,
+                    false,
+                    false,
+                    NULL
+#endif
+                    ,conf_get_str(ssh->conf, CONF_loghost),
+                    conf_get_bool(ssh->conf, CONF_change_password) // WINSCP
+                    );
                 ssh_connect_ppl(ssh, userauth_layer);
                 transport_child_layer = userauth_layer;
 
@@ -269,8 +282,12 @@ static void ssh_got_ssh_version(struct ssh_version_receiver *rcv,
                 ssh->fullhostname,
                 ssh_verstring_get_local(old_bpp),
                 ssh_verstring_get_remote(old_bpp),
+#ifndef NO_GSSAPI
                 &ssh->gss_state,
-                &ssh->stats, transport_child_layer, false);
+#else
+                NULL,
+#endif
+                &ssh->stats, transport_child_layer, NULL);
             ssh_connect_ppl(ssh, ssh->base_layer);
 
             if (userauth_layer)
@@ -544,6 +561,24 @@ void ssh_user_close(Ssh *ssh, const char *fmt, ...)
     }
 }
 
+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);
+    sfree(msg);
+}
+
+void ssh_sw_abort_deferred(Ssh *ssh, const char *fmt, ...)
+{
+    if (!ssh->deferred_abort_message) {
+        GET_FORMATTED_MSG;
+        ssh->deferred_abort_message = msg;
+        queue_toplevel_callback(get_seat_callback_set(ssh->seat), ssh_deferred_abort_callback, ssh);
+    }
+}
+
 static void ssh_socket_log(Plug *plug, int type, SockAddr *addr, int port,
                            const char *error_msg, int error_code)
 {
@@ -569,7 +604,7 @@ static void ssh_closing(Plug *plug, const char *error_msg, int error_code,
 {
     Ssh *ssh = container_of(plug, Ssh, plug);
     if (error_msg) {
-        ssh_remote_error(ssh, "Network error: %s", error_msg);
+        ssh_remote_error(ssh, "%s", error_msg);
     } else if (ssh->bpp) {
         ssh->bpp->input_eof = true;
         queue_idempotent_callback(&ssh->bpp->ic_in_raw);
@@ -910,6 +945,8 @@ static void ssh_free(Backend *be)
 	ssh_gss_cleanup(ssh->gss_state.libs);
 #endif
 
+    sfree(ssh->deferred_abort_message);
+
     delete_callbacks_for_context(get_seat_callback_set(ssh->seat), ssh); /* likely to catch ic_out_raw */ // WINSCP (seat)
 
     need_random_unref = ssh->need_random_unref;

+ 29 - 8
source/putty/ssh.h

@@ -413,12 +413,25 @@ 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, ...);
 
-#define SSH_CIPHER_IDEA		1
-#define SSH_CIPHER_DES		2
-#define SSH_CIPHER_3DES		3
-#define SSH_CIPHER_BLOWFISH	6
+/* Bit positions in the SSH-1 cipher protocol word */
+#define SSH1_CIPHER_IDEA        1
+#define SSH1_CIPHER_DES         2
+#define SSH1_CIPHER_3DES        3
+#define SSH1_CIPHER_BLOWFISH    6
+
+/* The subset of those that we support, with names for selecting them
+ * on Uppity's command line */
+#define SSH1_SUPPORTED_CIPHER_LIST(X)           \
+    X(SSH1_CIPHER_3DES, "3des")                 \
+    X(SSH1_CIPHER_BLOWFISH, "blowfish")         \
+    X(SSH1_CIPHER_DES, "des")                   \
+    /* end of list */
+#define SSH1_CIPHER_LIST_MAKE_MASK(bitpos, name) | (1U << bitpos)
+#define SSH1_SUPPORTED_CIPHER_MASK \
+    (0 SSH1_SUPPORTED_CIPHER_LIST(SSH1_CIPHER_LIST_MAKE_MASK))
 
 struct ssh_key {
     const ssh_keyalg *vt;
@@ -457,6 +470,7 @@ struct ec_mcurve
 {
     MontgomeryCurve *mc;
     MontgomeryPoint *G;
+    unsigned log2_cofactor;
 };
 
 /* Edwards form curve */
@@ -659,10 +673,10 @@ static inline void ssh_cipher_decrypt(ssh_cipher *c, void *blk, int len)
 { c->vt->decrypt(c, blk, len); }
 static inline void ssh_cipher_encrypt_length(
     ssh_cipher *c, void *blk, int len, unsigned long seq)
-{ /*WINSCP return*/ c->vt->encrypt_length(c, blk, len, seq); }
+{ c->vt->encrypt_length(c, blk, len, seq); }
 static inline void ssh_cipher_decrypt_length(
     ssh_cipher *c, void *blk, int len, unsigned long seq)
-{ /*WINSCP return*/ c->vt->decrypt_length(c, blk, len, seq); }
+{ c->vt->decrypt_length(c, blk, len, seq); }
 static inline const struct ssh_cipheralg *ssh_cipher_alg(ssh_cipher *c)
 { return c->vt; }
 
@@ -742,9 +756,9 @@ static inline ssh_hash *ssh_hash_new(const ssh_hashalg *alg)
 static inline ssh_hash *ssh_hash_copy(ssh_hash *h)
 { return h->vt->copy(h); }
 static inline void ssh_hash_final(ssh_hash *h, unsigned char *out)
-{ /*WINSCP return*/ h->vt->final(h, out); }
+{ h->vt->final(h, out); }
 static inline void ssh_hash_free(ssh_hash *h)
-{ /*WINSCP return*/ h->vt->free(h); }
+{ h->vt->free(h); }
 static inline const ssh_hashalg *ssh_hash_alg(ssh_hash *h)
 { return h->vt; }
 
@@ -770,6 +784,13 @@ struct ssh_kexes {
     const ssh_kex *const *list;
 };
 
+/* Indices of the negotiation strings in the KEXINIT packet */
+enum kexlist {
+    KEXLIST_KEX, KEXLIST_HOSTKEY, KEXLIST_CSCIPHER, KEXLIST_SCCIPHER,
+    KEXLIST_CSMAC, KEXLIST_SCMAC, KEXLIST_CSCOMP, KEXLIST_SCCOMP,
+    NKEXLIST
+};
+
 struct ssh_keyalg {
     /* Constructors that create an ssh_key */
     ssh_key *(*new_pub) (const ssh_keyalg *self, ptrlen pub);

+ 3 - 2
source/putty/ssh1bpp.c

@@ -42,6 +42,7 @@ static const struct BinaryPacketProtocolVtable ssh1_bpp_vtable = {
     ssh1_bpp_handle_output,
     ssh1_bpp_new_pktout,
     ssh1_bpp_queue_disconnect,
+    0xFFFFFFFF, /* no special packet size limit for this bpp */
 };
 
 BinaryPacketProtocol *ssh1_bpp_new(LogContext *logctx)
@@ -143,9 +144,9 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
             s->len = toint(GET_32BIT_MSB_FIRST(lenbuf));
         }
 
-        if (s->len < 0 || s->len > 262144) { /* SSH1.5-mandated max size */
+        if (s->len < 5 || s->len > 262144) { /* SSH1.5-mandated max size */
             ssh_sw_abort(s->bpp.ssh,
-                         "Extremely large packet length from remote suggests"
+                         "Out-of-range packet length from remote suggests"
                          " data stream corruption");
             crStopV;
         }

+ 5 - 0
source/putty/ssh1connection-client.c

@@ -533,3 +533,8 @@ SshChannel *ssh1_serverside_agent_open(ConnectionLayer *cl, Channel *chan)
 {
     unreachable("Should never be called in the client");
 }
+
+bool ssh1_connection_need_antispoof_prompt(struct ssh1_connection_state *s)
+{
+    return !seat_set_trust_status(s->ppl.seat, false);
+}

+ 41 - 0
source/putty/ssh1connection.c

@@ -200,6 +200,8 @@ static void ssh1_connection_free(PacketProtocolLayer *ppl)
     while ((c = delpos234(s->channels, 0)) != NULL)
         ssh1_channel_free(c);
     freetree234(s->channels);
+    if (s->mainchan_chan)
+        chan_free(s->mainchan_chan);
 
     if (s->x11disp)
 	x11_free_display(s->x11disp);
@@ -212,6 +214,9 @@ static void ssh1_connection_free(PacketProtocolLayer *ppl)
     freetree234(s->rportfwds);
     portfwdmgr_free(s->portfwdmgr);
 
+    if (s->antispoof_prompt)
+        free_prompts(s->antispoof_prompt);
+
     delete_callbacks_for_context(ppl->seat, s);
 
     sfree(s);
@@ -379,6 +384,42 @@ static void ssh1_connection_process_queue(PacketProtocolLayer *ppl)
 
     crBegin(s->crState);
 
+    /*
+     * Signal the seat that authentication is done, so that it can
+     * deploy spoofing defences. If it doesn't have any, deploy our
+     * own fallback one.
+     *
+     * We do this here rather than at the end of userauth, because we
+     * might not have gone through userauth at all (if we're a
+     * connection-sharing downstream).
+     */
+    if (ssh1_connection_need_antispoof_prompt(s)) {
+        s->antispoof_prompt = new_prompts();
+        s->antispoof_prompt->to_server = true;
+        s->antispoof_prompt->from_server = false;
+        s->antispoof_prompt->name = dupstr("Authentication successful");
+        add_prompt(
+            s->antispoof_prompt,
+            dupstr("Access granted. Press Return to begin session. "), false);
+        s->antispoof_ret = seat_get_userpass_input(
+            s->ppl.seat, s->antispoof_prompt, NULL);
+        while (1) {
+            while (s->antispoof_ret < 0 &&
+                   bufchain_size(s->ppl.user_input) > 0)
+                s->antispoof_ret = seat_get_userpass_input(
+                    s->ppl.seat, s->antispoof_prompt, s->ppl.user_input);
+
+            if (s->antispoof_ret >= 0)
+                break;
+
+            s->want_user_input = true;
+            crReturnV;
+            s->want_user_input = false;
+        }
+        free_prompts(s->antispoof_prompt);
+        s->antispoof_prompt = NULL;
+    }
+
     portfwdmgr_config(s->portfwdmgr, s->conf);
     s->portfwdmgr_configured = true;
 

+ 7 - 0
source/putty/ssh1connection.h

@@ -52,6 +52,11 @@ struct ssh1_connection_state {
     bool compressing;                  /* used in server mode only */
     bool sent_exit_status;             /* also for server mode */
 
+    prompts_t *antispoof_prompt;
+    int antispoof_ret;
+
+    const SshServerConfig *ssc;
+
     ConnectionLayer cl;
     PacketProtocolLayer ppl;
 };
@@ -118,3 +123,5 @@ bool ssh1_handle_direction_specific_packet(
     struct ssh1_connection_state *s, PktIn *pktin);
 
 bool ssh1_check_termination(struct ssh1_connection_state *s);
+
+bool ssh1_connection_need_antispoof_prompt(struct ssh1_connection_state *s);

+ 14 - 11
source/putty/ssh1login.c

@@ -217,8 +217,11 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
         return;
     }
 
-    s->len = (s->hostkey.bytes > s->servkey.bytes ?
-              s->hostkey.bytes : s->servkey.bytes);
+    s->len = 32;
+    if (s->len < s->hostkey.bytes)
+        s->len = s->hostkey.bytes;
+    if (s->len < s->servkey.bytes)
+        s->len = s->servkey.bytes;
 
     s->rsabuf = snewn(s->len, unsigned char);
 
@@ -300,11 +303,11 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                 ppl_logevent("AES not supported in SSH-1, skipping");
             } else {
                 switch (next_cipher) {
-                  case CIPHER_3DES:     s->cipher_type = SSH_CIPHER_3DES;
+                  case CIPHER_3DES:     s->cipher_type = SSH1_CIPHER_3DES;
                     cipher_string = "3DES"; break;
-                  case CIPHER_BLOWFISH: s->cipher_type = SSH_CIPHER_BLOWFISH;
+                  case CIPHER_BLOWFISH: s->cipher_type = SSH1_CIPHER_BLOWFISH;
                     cipher_string = "Blowfish"; break;
-                  case CIPHER_DES:      s->cipher_type = SSH_CIPHER_DES;
+                  case CIPHER_DES:      s->cipher_type = SSH1_CIPHER_DES;
                     cipher_string = "single-DES"; break;
                 }
                 if (s->supported_ciphers_mask & (1 << s->cipher_type))
@@ -312,7 +315,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
             }
         }
         if (!cipher_chosen) {
-            if ((s->supported_ciphers_mask & (1 << SSH_CIPHER_3DES)) == 0) {
+            if ((s->supported_ciphers_mask & (1 << SSH1_CIPHER_3DES)) == 0) {
                 ssh_proto_error(s->ppl.ssh, "Server violates SSH-1 protocol "
                                 "by not supporting 3DES encryption");
             } else {
@@ -336,13 +339,13 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
     }
 
     switch (s->cipher_type) {
-      case SSH_CIPHER_3DES:
+      case SSH1_CIPHER_3DES:
         ppl_logevent("Using 3DES encryption");
         break;
-      case SSH_CIPHER_DES:
+      case SSH1_CIPHER_DES:
         ppl_logevent("Using single-DES encryption");
         break;
-      case SSH_CIPHER_BLOWFISH:
+      case SSH1_CIPHER_BLOWFISH:
         ppl_logevent("Using Blowfish encryption");
         break;
     }
@@ -369,8 +372,8 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
 
     {
         const ssh_cipheralg *cipher =
-            (s->cipher_type == SSH_CIPHER_BLOWFISH ? &ssh_blowfish_ssh1 :
-             s->cipher_type == SSH_CIPHER_DES ? &ssh_des : &ssh_3des_ssh1);
+            (s->cipher_type == SSH1_CIPHER_BLOWFISH ? &ssh_blowfish_ssh1 :
+             s->cipher_type == SSH1_CIPHER_DES ? &ssh_des : &ssh_3des_ssh1);
         ssh1_bpp_new_cipher(s->ppl.bpp, cipher, s->session_key);
     }
 

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

@@ -31,6 +31,7 @@ static const struct BinaryPacketProtocolVtable ssh2_bare_bpp_vtable = {
     ssh2_bare_bpp_handle_output,
     ssh2_bare_bpp_new_pktout,
     ssh2_bpp_queue_disconnect, /* in sshcommon.c */
+    0x4000, /* packet size limit, per protocol spec in sshshare.c comment */
 };
 
 BinaryPacketProtocol *ssh2_bare_bpp_new(LogContext *logctx)

+ 1 - 0
source/putty/ssh2bpp.c

@@ -52,6 +52,7 @@ static const struct BinaryPacketProtocolVtable ssh2_bpp_vtable = {
     ssh2_bpp_handle_output,
     ssh2_bpp_new_pktout,
     ssh2_bpp_queue_disconnect, /* in sshcommon.c */
+    0xFFFFFFFF, /* no special packet size limit for this bpp */
 };
 
 BinaryPacketProtocol *ssh2_bpp_new(

+ 21 - 0
source/putty/ssh2connection.c

@@ -314,6 +314,11 @@ static void ssh2_connection_free(PacketProtocolLayer *ppl)
     }
     portfwdmgr_free(s->portfwdmgr);
 
+    if (s->antispoof_prompt)
+        free_prompts(s->antispoof_prompt);
+
+    delete_callbacks_for_context(get_log_callback_set(s->cl.logctx), s);
+
     sfree(s);
 }
 
@@ -420,6 +425,8 @@ static bool ssh2_connection_filter_queue(struct ssh2_connection_state *s)
                 ssh2_channel_init(c);
                 c->remwindow = winsize;
                 c->remmaxpkt = pktsize;
+                if (c->remmaxpkt > s->ppl.bpp->vt->packet_size_limit)
+                    c->remmaxpkt = s->ppl.bpp->vt->packet_size_limit;
                 if (c->chan->initial_fixed_window_size) {
                     c->locwindow = c->locmaxwin = c->remlocwin =
                         c->chan->initial_fixed_window_size;
@@ -486,6 +493,8 @@ static bool ssh2_connection_filter_queue(struct ssh2_connection_state *s)
                 c->halfopen = false;
                 c->remwindow = get_uint32(pktin);
                 c->remmaxpkt = get_uint32(pktin);
+                if (c->remmaxpkt > s->ppl.bpp->vt->packet_size_limit)
+                    c->remmaxpkt = s->ppl.bpp->vt->packet_size_limit;
 
                 chan_open_confirmation(c->chan);
 
@@ -539,6 +548,17 @@ static bool ssh2_connection_filter_queue(struct ssh2_connection_state *s)
                         c->chan, ext_type == SSH2_EXTENDED_DATA_STDERR,
                         data.ptr, data.len);
 
+                    /*
+                     * The channel may have turned into a connection-
+                     * shared one as a result of that chan_send, e.g.
+                     * if the data we just provided completed the X11
+                     * auth phase and caused a callback to
+                     * x11_sharing_handover. If so, do nothing
+                     * further.
+                     */
+                    if (c->sharectx)
+                        break;
+
                     /*
                      * If it looks like the remote end hit the end of
                      * its window, and we didn't want it to do that,
@@ -990,6 +1010,7 @@ static void ssh2_connection_process_queue(PacketProtocolLayer *ppl)
             s->want_user_input = false;
         }
         free_prompts(s->antispoof_prompt);
+        s->antispoof_prompt = NULL;
     }
 
     /*

+ 1 - 0
source/putty/ssh2connection.h

@@ -42,6 +42,7 @@ struct ssh2_connection_state {
     int antispoof_ret;
 
     const SftpServerVtable *sftpserver_vt;
+    const SshServerConfig *ssc;
 
     /*
      * These store the list of global requests that we're waiting for

+ 2 - 3
source/putty/ssh2kex-client.c

@@ -412,9 +412,6 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
                 data = get_string(pktin);
                 s->mic.value = (char *)data.ptr;
                 s->mic.length = data.len;
-                /* Save expiration time of cred when delegating */
-                if (s->gss_delegate && s->gss_cred_expiry != GSS_NO_EXPIRATION)
-                    s->gss_cred_expiry = s->gss_cred_expiry;
                 /* If there's a final token we loop to consume it */
                 if (get_bool(pktin)) {
                     data = get_string(pktin);
@@ -552,6 +549,7 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
             *aborted = true;
             return;
         }
+        s->rsa_kex_key_needs_freeing = true;
 
         put_stringpl(s->exhash, rsakeydata);
 
@@ -615,6 +613,7 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
 
         ssh_rsakex_freekey(s->rsa_kex_key);
         s->rsa_kex_key = NULL;
+        s->rsa_kex_key_needs_freeing = false;
 
         crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
         if (pktin->type != SSH2_MSG_KEXRSA_DONE) {

+ 1 - 0
source/putty/ssh2transhk.c

@@ -76,6 +76,7 @@ void ssh_transient_hostkey_cache_add(
 
     if ((ent = find234(thc->cache, (void *)ssh_key_alg(key),
                        ssh_transient_hostkey_cache_find)) != NULL) {
+        del234(thc->cache, ent);
         strbuf_free(ent->pub_blob);
         sfree(ent);
     }

+ 45 - 14
source/putty/ssh2transport.c

@@ -9,6 +9,9 @@
 #include "sshbpp.h"
 #include "sshppl.h"
 #include "sshcr.h"
+#ifndef WINSCP
+#include "sshserver.h"
+#endif
 #include "storage.h"
 #include "ssh2transport.h"
 #include "mpint.h"
@@ -117,7 +120,7 @@ PacketProtocolLayer *ssh2_transport_new(
     const char *client_greeting, const char *server_greeting,
     struct ssh_connection_shared_gss_state *shgss,
     struct DataTransferStats *stats, PacketProtocolLayer *higher_layer,
-    bool is_server)
+    const SshServerConfig *ssc)
 {
     struct ssh2_transport_state *s = snew(struct ssh2_transport_state);
     memset(s, 0, sizeof(*s));
@@ -154,13 +157,18 @@ PacketProtocolLayer *ssh2_transport_new(
 
     s->outgoing_kexinit = strbuf_new();
     s->incoming_kexinit = strbuf_new();
-    if (is_server) {
+    if (ssc) {
+        s->ssc = ssc;
         s->client_kexinit = s->incoming_kexinit;
         s->server_kexinit = s->outgoing_kexinit;
+        s->cstrans = &s->in;
+        s->sctrans = &s->out;
         s->out.mkkey_adjust = 1;
     } else {
         s->client_kexinit = s->outgoing_kexinit;
         s->server_kexinit = s->incoming_kexinit;
+        s->cstrans = &s->out;
+        s->sctrans = &s->in;
         s->in.mkkey_adjust = 1;
     }
 
@@ -217,8 +225,10 @@ static void ssh2_transport_free(PacketProtocolLayer *ppl)
     if (s->K) mp_free(s->K);
     if (s->dh_ctx)
         dh_cleanup(s->dh_ctx);
-    if (s->rsa_kex_key)
+    if (s->rsa_kex_key_needs_freeing) {
         ssh_rsakex_freekey(s->rsa_kex_key);
+        sfree(s->rsa_kex_key);
+    }
     if (s->ecdh_key)
         ssh_ecdhkex_freekey(s->ecdh_key);
     if (s->exhash)
@@ -421,7 +431,7 @@ PktIn *ssh2_transport_pop(struct ssh2_transport_state *s)
 static void ssh2_write_kexinit_lists(
     /*WINSCP*/ Seat * seat, BinarySink *pktout,
     struct kexinit_algorithm kexlists[NKEXLIST][MAXKEXLIST],
-    Conf *conf, int remote_bugs,
+    Conf *conf, const SshServerConfig *ssc, int remote_bugs,
     const char *hk_host, int hk_port, const ssh_keyalg *hk_prev,
     ssh_transient_hostkey_cache *thc,
     ssh_key *const *our_hostkeys, int our_nhostkeys,
@@ -742,10 +752,18 @@ static void ssh2_write_kexinit_lists(
      */
     for (i = 0; i < NKEXLIST; i++) {
         strbuf *list = strbuf_new();
-        for (j = 0; j < MAXKEXLIST; j++) {
-            if (kexlists[i][j].name == NULL) break;
-            add_to_commasep(list, kexlists[i][j].name);
+        #ifndef WINSCP
+        if (ssc && ssc->kex_override[i].ptr) {
+            put_datapl(list, ssc->kex_override[i]);
+        } else {
+        #endif
+            for (j = 0; j < MAXKEXLIST; j++) {
+                if (kexlists[i][j].name == NULL) break;
+                add_to_commasep(list, kexlists[i][j].name);
+            }
+        #ifndef WINSCP
         }
+        #endif
         put_stringsb(pktout, list);
     }
     /* List client->server languages. Empty list. */
@@ -826,12 +844,26 @@ static bool ssh2_scan_kexinits(
 
         selected[i] = NULL;
         for (j = 0; j < MAXKEXLIST; j++) {
-            if (ptrlen_eq_string(found, kexlists[i][j].name)) {
+            if (kexlists[i][j].name &&
+                ptrlen_eq_string(found, kexlists[i][j].name)) {
                 selected[i] = &kexlists[i][j];
                 break;
             }
         }
-        assert(selected[i]); /* kexlists[] must cover one of the inputs */
+        if (!selected[i]) {
+            /*
+             * In the client, this should never happen! But in the
+             * server, where we allow manual override on the command
+             * line of the exact KEXINIT strings, it can happen
+             * because the command line contained a typo. So we
+             * produce a reasonably useful message instead of an
+             * assertion failure.
+             */
+            ssh_sw_abort(ssh, "Selected %s \"%.*s\" does not correspond to "
+                         "any supported algorithm",
+                         kexlist_descr[i], PTRLEN_PRINTF(found));
+            return false;
+        }
 
         /*
          * If the kex or host key algorithm is not the first one in
@@ -1070,7 +1102,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
     random_read(strbuf_append(s->outgoing_kexinit, 16), 16);
     ssh2_write_kexinit_lists(
         /*WINSCP*/ s->ppl.seat, BinarySink_UPCAST(s->outgoing_kexinit), s->kexlists,
-        s->conf, s->ppl.remote_bugs,
+        s->conf, s->ssc, s->ppl.remote_bugs,
         s->savedhost, s->savedport, s->hostkey_alg, s->thc,
         s->hostkeys, s->nhostkeys,
         !s->got_session_id, s->can_gssapi_keyex,
@@ -1117,8 +1149,8 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
         if (!ssh2_scan_kexinits(
                 ptrlen_from_strbuf(s->client_kexinit),
                 ptrlen_from_strbuf(s->server_kexinit),
-                s->kexlists, &s->kex_alg, &s->hostkey_alg, &s->out, &s->in,
-                &s->warn_kex, &s->warn_hk, &s->warn_cscipher,
+                s->kexlists, &s->kex_alg, &s->hostkey_alg, s->cstrans,
+                s->sctrans, &s->warn_kex, &s->warn_hk, &s->warn_cscipher,
                 &s->warn_sccipher, s->ppl.ssh, NULL, &s->ignorepkt, &nhk, hks))
             return; /* false means a fatal error function was called */
 
@@ -1791,6 +1823,7 @@ static void ssh2_transport_gss_update(struct ssh2_transport_state *s,
     if (mins > 0 && s->gss_ctxt_lifetime <= mins * 60)
         s->gss_status |= GSS_CTXT_EXPIRES;
 }
+#endif /* NO_GSSAPI */
 
 ptrlen ssh2_transport_get_session_id(PacketProtocolLayer *ppl)
 {
@@ -1815,8 +1848,6 @@ void ssh2_transport_notify_auth_done(PacketProtocolLayer *ppl)
     queue_idempotent_callback(&s->ppl.ic_process_queue);
 }
 
-#endif /* NO_GSSAPI */
-
 static bool ssh2_transport_get_specials(
     PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx)
 {

+ 4 - 6
source/putty/ssh2transport.h

@@ -18,11 +18,6 @@
 #define DH_MIN_SIZE 1024
 #define DH_MAX_SIZE 8192
 
-enum kexlist {
-    KEXLIST_KEX, KEXLIST_HOSTKEY, KEXLIST_CSCIPHER, KEXLIST_SCCIPHER,
-    KEXLIST_CSMAC, KEXLIST_SCMAC, KEXLIST_CSCOMP, KEXLIST_SCCOMP,
-    NKEXLIST
-};
 #define MAXKEXLIST 16
 struct kexinit_algorithm {
     const char *name;
@@ -140,6 +135,8 @@ struct ssh2_transport_state {
 
     struct DataTransferStats *stats;
 
+    const SshServerConfig *ssc;
+
     char *client_greeting, *server_greeting;
 
     bool kex_in_progress;
@@ -171,12 +168,13 @@ struct ssh2_transport_state {
     strbuf *outgoing_kexinit, *incoming_kexinit;
     strbuf *client_kexinit, *server_kexinit; /* aliases to the above */
     int kex_init_value, kex_reply_value;
-    transport_direction in, out;
+    transport_direction in, out, *cstrans, *sctrans;
     ptrlen hostkeydata, sigdata;
     strbuf *hostkeyblob;
     char *keystr, *fingerprint;
     ssh_key *hkey;                     /* actual host key */
     RSAKey *rsa_kex_key;             /* for RSA kex */
+    bool rsa_kex_key_needs_freeing;
     ecdh_key *ecdh_key;                     /* for ECDH kex */
     unsigned char exchange_hash[MAX_HASH_LEN];
     bool can_gssapi_keyex;

+ 41 - 9
source/putty/ssh2userauth.c

@@ -23,7 +23,7 @@ struct ssh2_userauth_state {
 
     PacketProtocolLayer *transport_layer, *successor_layer;
     Filename *keyfile;
-    bool tryagent, change_username;
+    bool show_banner, tryagent, change_username;
     char *hostname, *fullhostname;
     char *default_username;
     bool try_ki_auth, try_gssapi_auth, try_gssapi_kex_auth, gssapi_fwd;
@@ -59,8 +59,9 @@ struct ssh2_userauth_state {
     strbuf *last_methods_string;
     bool kbd_inter_refused;
     prompts_t *cur_prompt;
-    int num_prompts;
-    char *username;
+    uint32_t num_prompts;
+    const char *username;
+    char *locally_allocated_username;
     char *password;
     bool got_username;
     strbuf *publickey_blob;
@@ -127,7 +128,7 @@ static const struct PacketProtocolLayerVtable ssh2_userauth_vtable = {
 PacketProtocolLayer *ssh2_userauth_new(
     PacketProtocolLayer *successor_layer,
     const char *hostname, const char *fullhostname,
-    Filename *keyfile, bool tryagent,
+    Filename *keyfile, bool show_banner, bool tryagent,
     const char *default_username, bool change_username,
     bool try_ki_auth, bool try_gssapi_auth, bool try_gssapi_kex_auth,
     bool gssapi_fwd, struct ssh_connection_shared_gss_state *shgss,
@@ -141,6 +142,7 @@ PacketProtocolLayer *ssh2_userauth_new(
     s->hostname = dupstr(hostname);
     s->fullhostname = dupstr(fullhostname);
     s->keyfile = filename_copy(keyfile);
+    s->show_banner = show_banner;
     s->tryagent = tryagent;
     s->default_username = dupstr(default_username);
     s->change_username = change_username;
@@ -180,8 +182,13 @@ static void ssh2_userauth_free(PacketProtocolLayer *ppl)
         agent_cancel_query(s->auth_agent_query);
     filename_free(s->keyfile);
     sfree(s->default_username);
+    sfree(s->locally_allocated_username);
     sfree(s->hostname);
     sfree(s->fullhostname);
+    sfree(s->publickey_comment);
+    sfree(s->publickey_algorithm);
+    if (s->publickey_blob)
+        strbuf_free(s->publickey_blob);
     strbuf_free(s->last_methods_string);
     sfree(s->loghost);
     if (s->banner_scc)
@@ -199,6 +206,11 @@ static void ssh2_userauth_filter_queue(struct ssh2_userauth_state *s)
     while ((pktin = pq_peek(s->ppl.in_pq)) != NULL) {
         switch (pktin->type) {
           case SSH2_MSG_USERAUTH_BANNER:
+            if (!s->show_banner) {
+                pq_pop(s->ppl.in_pq);
+                break;
+            }
+
             string = get_string(pktin);
             if (string.len > BANNER_LIMIT - bufchain_size(&s->banner))
                 string.len = BANNER_LIMIT - bufchain_size(&s->banner);
@@ -388,7 +400,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
         /*
          * Get a username.
          */
-        if (s->got_username && s->change_username) {
+        if (s->got_username && !s->change_username) {
             /*
              * We got a username last time round this loop, and
              * with change_username turned off we don't try to get
@@ -424,7 +436,9 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                 ssh_user_close(s->ppl.ssh, "No username provided");
                 return;
             }
-            s->username = dupstr(s->cur_prompt->prompts[0]->result);
+            sfree(s->locally_allocated_username); /* for change_username */
+            s->username = s->locally_allocated_username =
+                dupstr(s->cur_prompt->prompts[0]->result);
             free_prompts(s->cur_prompt);
         } else {
             if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE))
@@ -626,8 +640,10 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                  */
                 { // WINSCP
                 bool srv_pubkey = false, srv_passwd = false;
-                bool srv_keyb_inter = false, srv_gssapi = false;
-                bool srv_gssapi_keyex_auth = false;
+                bool srv_keyb_inter = false;
+#ifndef NO_GSSAPI
+                bool srv_gssapi = false, srv_gssapi_keyex_auth = false;
+#endif
 
                 ptrlen method; // WINSCP
                 for (; get_commasep_word(&methods, &method) ;) {
@@ -637,10 +653,12 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                         srv_passwd = true;
                     else if (ptrlen_eq_string(method, "keyboard-interactive"))
                         srv_keyb_inter = true;
+#ifndef NO_GSSAPI
                     else if (ptrlen_eq_string(method, "gssapi-with-mic"))
                         srv_gssapi = true;
                     else if (ptrlen_eq_string(method, "gssapi-keyex"))
                         srv_gssapi_keyex_auth = true;
+#endif
                 }
 
                 /*
@@ -1235,7 +1253,6 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
 
                     ptrlen name, inst;
                     strbuf *sb;
-                    int i;
 
                     /*
                      * We've got a fresh USERAUTH_INFO_REQUEST.
@@ -1252,10 +1269,19 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                      * Get any prompt(s) from the packet.
                      */
                     s->num_prompts = get_uint32(pktin);
+                    { // WINSCP
+                    uint32_t i;
                     for (i = 0; i < s->num_prompts; i++) {
                         ptrlen prompt = get_string(pktin);
                         bool echo = get_bool(pktin);
 
+                        if (get_err(pktin)) {
+                            ssh_proto_error(
+                                s->ppl.ssh, "Server sent truncated "
+                                "SSH_MSG_USERAUTH_INFO_REQUEST packet");
+                            return;
+                        }
+
                         sb = strbuf_new();
                         if (!prompt.len) {
                             put_datapl(sb, PTRLEN_LITERAL(
@@ -1270,6 +1296,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                         }
                         add_prompt(s->cur_prompt, strbuf_to_str(sb), echo);
                     }
+                    } // WINSCP
 
                     /*
                      * Make the header strings. This includes the
@@ -1322,6 +1349,8 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                     } else {
                         s->cur_prompt->instr_reqd = false;
                     }
+                    if (sb->len)
+                        s->cur_prompt->instruction = strbuf_to_str(sb);
 
                     /*
                      * Our prompts_t is fully constructed now. Get the
@@ -1361,10 +1390,13 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                     s->pktout = ssh_bpp_new_pktout(
                         s->ppl.bpp, SSH2_MSG_USERAUTH_INFO_RESPONSE);
                     put_uint32(s->pktout, s->num_prompts);
+                    { // WINSCP
+                    uint32_t i;
                     for (i=0; i < s->num_prompts; i++) {
                         put_stringz(s->pktout,
                                     s->cur_prompt->prompts[i]->result);
                     }
+                    } // WINSCP
                     s->pktout->minlen = 256;
                     pq_push(s->ppl.out_pq, s->pktout);
 

+ 9 - 7
source/putty/sshaes.c

@@ -54,15 +54,17 @@
 #       define USE_CLANG_ATTR_TARGET_AARCH64
 #   endif
 #elif defined _MSC_VER
-    /* Visual Studio supports the crypto extension when targeting
-     * AArch64, but as of VS2017, the AArch32 header doesn't quite
-     * manage it (declaring the aese/aesd intrinsics without a round
-     * key operand). */
 #   if defined _M_ARM64
 #       define HW_AES HW_AES_NEON
-#       if defined _M_ARM64
-#           define USE_ARM64_NEON_H /* unusual header name in this case */
-#       endif
+        /* 64-bit Visual Studio uses the header <arm64_neon.h> in place
+         * of the standard <arm_neon.h> */
+#       define USE_ARM64_NEON_H
+#   elif defined _M_ARM
+#       define HW_AES HW_AES_NEON
+        /* 32-bit Visual Studio uses the right header name, but requires
+         * this #define to enable a set of intrinsic definitions that
+         * do not omit one of the parameters for vaes[ed]q_u8 */
+#       define _ARM_USE_NEW_NEON_INTRINSICS
 #   endif
 #endif
 

+ 1 - 0
source/putty/sshbpp.h

@@ -12,6 +12,7 @@ struct BinaryPacketProtocolVtable {
     PktOut *(*new_pktout)(int type);
     void (*queue_disconnect)(BinaryPacketProtocol *,
                              const char *msg, int category);
+    uint32_t packet_size_limit;
 };
 
 struct BinaryPacketProtocol {

+ 2 - 2
source/putty/sshchan.h

@@ -250,8 +250,8 @@ static inline void sshfwd_x11_sharing_handover(
     SshChannel *c, ssh_sharing_connstate *cs, share_channel *sch,
     const char *addr, int port, int endian, int maj, int min,
     const void *idata, int ilen)
-{ /*WINSCP return*/ c->vt->x11_sharing_handover(c, cs, sch, addr, port, endian,
-                                     maj, min, idata, ilen); }
+{ c->vt->x11_sharing_handover(c, cs, sch, addr, port, endian,
+                              maj, min, idata, ilen); }
 static inline void sshfwd_send_exit_status(SshChannel *c, int status)
 { c->vt->send_exit_status(c, status); }
 static inline void sshfwd_send_exit_signal(

+ 25 - 6
source/putty/sshecc.c

@@ -96,11 +96,12 @@ static void initialise_wcurve(
 
 static void initialise_mcurve(
     struct ec_curve *curve, mp_int *p, mp_int *a, mp_int *b,
-    mp_int *G_x)
+    mp_int *G_x, unsigned log2_cofactor)
 {
     initialise_common(curve, EC_MONTGOMERY, p);
 
     curve->m.mc = ecc_montgomery_curve(p, a, b);
+    curve->m.log2_cofactor = log2_cofactor;
 
     curve->m.G = ecc_montgomery_point_new(curve->m.mc, G_x);
 }
@@ -260,7 +261,7 @@ static struct ec_curve *ec_curve25519(void)
         mp_int *a = MP_LITERAL(0x0000000000000000000000000000000000000000000000000000000000076d06);
         mp_int *b = MP_LITERAL(0x0000000000000000000000000000000000000000000000000000000000000001);
         mp_int *G_x = MP_LITERAL(0x0000000000000000000000000000000000000000000000000000000000000009);
-        initialise_mcurve(&curve, p, a, b, G_x);
+        initialise_mcurve(&curve, p, a, b, G_x, 3);
         mp_free(p);
         mp_free(a);
         mp_free(b);
@@ -932,7 +933,7 @@ static ssh_key *ecdsa_new_priv_openssh(
 
     ek->publicKey = get_wpoint(src, curve);
     if (!ek->publicKey) {
-        eddsa_freekey(&ek->sshk);
+        ecdsa_freekey(&ek->sshk);
         return NULL;
     }
 
@@ -1479,10 +1480,20 @@ static void ssh_ecdhkex_m_setup(ecdh_key *dh)
     random_read(strbuf_append(bytes, dh->curve->fieldBytes),
                 dh->curve->fieldBytes);
 
-    bytes->u[0] &= 0xF8;
-    bytes->u[bytes->len-1] &= 0x7F;
-    bytes->u[bytes->len-1] |= 0x40;
     dh->private = mp_from_bytes_le(ptrlen_from_strbuf(bytes));
+
+    /* Ensure the private key has the highest valid bit set, and no
+     * bits _above_ the highest valid one */
+    mp_reduce_mod_2to(dh->private, dh->curve->fieldBits);
+    mp_set_bit(dh->private, dh->curve->fieldBits - 1, 1);
+
+    /* Clear a curve-specific number of low bits */
+    { // WINSCP
+    unsigned bit;
+    for (bit = 0; bit < dh->curve->m.log2_cofactor; bit++)
+        mp_set_bit(dh->private, bit, 0);
+    } // WINSCP
+
     strbuf_free(bytes);
 
     dh->m_public = ecc_montgomery_multiply(dh->curve->m.G, dh->private);
@@ -1548,6 +1559,14 @@ static mp_int *ssh_ecdhkex_w_getkey(ecdh_key *dh, ptrlen remoteKey)
 static mp_int *ssh_ecdhkex_m_getkey(ecdh_key *dh, ptrlen remoteKey)
 {
     mp_int *remote_x = mp_from_bytes_le(remoteKey);
+
+    /* Per RFC 7748 section 5, discard any set bits of the other
+     * side's public value beyond the minimum number of bits required
+     * to represent all valid values. However, an overlarge value that
+     * still fits into the remaining number of bits is accepted, and
+     * will be reduced mod p. */
+    mp_reduce_mod_2to(remote_x, dh->curve->fieldBits);
+
     if (mp_eq_integer(remote_x, 0)) {
         /*
          * The libssh spec for Curve25519 key exchange says that

+ 2 - 2
source/putty/sshppl.h

@@ -107,11 +107,11 @@ PacketProtocolLayer *ssh2_transport_new(
     const char *client_greeting, const char *server_greeting,
     struct ssh_connection_shared_gss_state *shgss,
     struct DataTransferStats *stats, PacketProtocolLayer *higher_layer,
-    bool is_server);
+    const SshServerConfig *ssc);
 PacketProtocolLayer *ssh2_userauth_new(
     PacketProtocolLayer *successor_layer,
     const char *hostname, const char *fullhostname,
-    Filename *keyfile, bool tryagent,
+    Filename *keyfile, bool show_banner, bool tryagent,
     const char *default_username, bool change_username,
     bool try_ki_auth,
     bool try_gssapi_auth, bool try_gssapi_kex_auth,

+ 2 - 2
source/putty/sshpubk.c

@@ -71,7 +71,7 @@ static int rsa_ssh1_load_main(FILE * fp, RSAKey *key, bool pub_only,
 
     /* One byte giving encryption type, and one reserved uint32. */
     ciphertype = get_byte(src);
-    if (ciphertype != 0 && ciphertype != SSH_CIPHER_3DES)
+    if (ciphertype != 0 && ciphertype != SSH1_CIPHER_3DES)
 	goto end;
     if (get_uint32(src) != 0)
         goto end;                 /* reserved field nonzero, panic! */
@@ -330,7 +330,7 @@ bool rsa_ssh1_savekey(const Filename *filename, RSAKey *key,
      * The public part of the key.
      */
     put_data(buf, rsa_signature, sizeof(rsa_signature));
-    put_byte(buf, passphrase ? SSH_CIPHER_3DES : 0); /* encryption type */
+    put_byte(buf, passphrase ? SSH1_CIPHER_3DES : 0); /* encryption type */
     put_uint32(buf, 0);                              /* reserved */
     rsa_ssh1_public_blob(BinarySink_UPCAST(buf), key,
                          RSA_SSH1_MODULUS_FIRST);

+ 25 - 5
source/putty/sshrand.c

@@ -4,6 +4,7 @@
 
 #include "putty.h"
 #include "ssh.h"
+#include "storage.h"
 #include <assert.h>
 
 /* Collect environmental noise every 5 minutes */
@@ -85,6 +86,18 @@ static void random_create(const ssh_hashalg *hashalg)
     random_save_seed();
 }
 
+void random_save_seed(void)
+{
+    int len;
+    void *data;
+
+    if (random_active) {
+	random_get_savedata(&data, &len);
+	write_random_seed(data, len);
+	sfree(data);
+    }
+}
+
 void random_ref(void)
 {
     WINSCP_PUTTY_SECTION_ENTER;
@@ -112,11 +125,10 @@ void random_reseed(ptrlen seed)
     WINSCP_PUTTY_SECTION_LEAVE;
 }
 
-void random_unref(void)
+// Never called directly in WINSCP
+void random_clear(void)
 {
-    WINSCP_PUTTY_SECTION_ENTER;
-    assert(random_active > 0);
-    if (random_active == 1) {
+    if (global_prng) {
         #ifndef WINSCP
         // We control this on our own in PuttyFinalize()
         random_save_seed();
@@ -124,8 +136,16 @@ void random_unref(void)
         expire_timer_context(&random_timer_ctx);
         prng_free(global_prng);
         global_prng = NULL;
+        random_active = 0;
     }
-    random_active--;
+}
+
+void random_unref(void)
+{
+    WINSCP_PUTTY_SECTION_ENTER;
+    assert(random_active > 0);
+    if (--random_active == 0)
+        random_clear();
     WINSCP_PUTTY_SECTION_LEAVE;
 }
 

+ 5 - 2
source/putty/sshrsa.c

@@ -312,8 +312,11 @@ bool rsa_verify(RSAKey *key)
     mp_int *n, *ed, *pm1, *qm1;
     unsigned ok = 1;
 
-    /* Preliminary checks: p,q must actually be nonzero. */
-    if (mp_eq_integer(key->p, 0) | mp_eq_integer(key->q, 0))
+    /* Preliminary checks: p,q can't be 0 or 1. (Of course no other
+     * very small value is any good either, but these are the values
+     * we _must_ check for to avoid assertion failures further down
+     * this function.) */
+    if (!(mp_hs_integer(key->p, 2) & mp_hs_integer(key->q, 2)))
         return false;
 
     /* n must equal pq. */

+ 0 - 2
source/putty/sshsha.c

@@ -712,8 +712,6 @@ struct sha1_neon_core {
     uint32_t e;
 };
 
-/* ------------- got up to here ----------------------------------------- */
-
 FUNC_ISA
 static inline uint32x4_t sha1_neon_load_input(const uint8_t *p)
 {

+ 1 - 0
source/putty/sshverstring.c

@@ -51,6 +51,7 @@ static const struct BinaryPacketProtocolVtable ssh_verstring_vtable = {
     ssh_verstring_handle_output,
     ssh_verstring_new_pktout,
     ssh_verstring_queue_disconnect,
+    0xFFFFFFFF, /* no special packet size limit for this bpp */
 };
 
 static void ssh_detect_bugs(struct ssh_verstring_state *s);

+ 50 - 3
source/putty/sshzlib.c

@@ -850,7 +850,51 @@ struct zlib_decompress_ctx {
 	lenrep;
     int uncomplen;
     unsigned char lenlen[19];
-    unsigned char lengths[286 + 32];
+
+    /*
+     * Array that accumulates the code lengths sent in the header of a
+     * dynamic-Huffman-tree block.
+     *
+     * There are 286 actual symbols in the literal/length alphabet
+     * (256 literals plus 20 length categories), and 30 symbols in the
+     * distance alphabet. However, the block header transmits the
+     * number of code lengths for the former alphabet as a 5-bit value
+     * HLIT to be added to 257, and the latter as a 5-bit value HDIST
+     * to be added to 1. This means that the number of _code lengths_
+     * can go as high as 288 for the symbol alphabet and 32 for the
+     * distance alphabet - each of those values being 2 more than the
+     * maximum number of actual symbols.
+     *
+     * It's tempting to rule that sending out-of-range HLIT or HDIST
+     * is therefore just illegal, and to fault it when we initially
+     * receive that header. But instead I've chosen to permit the
+     * Huffman-code definition to include code length entries for
+     * those unused symbols; if a header of that form is transmitted,
+     * then the effect will be that in the main body of the block,
+     * some bit sequence(s) will generate an illegal symbol number,
+     * and _that_ will be faulted as a decoding error.
+     *
+     * Rationale: this can already happen! The standard Huffman code
+     * used in a _static_ block for the literal/length alphabet is
+     * defined in such a way that it includes codes for symbols 287
+     * and 288, which are then never actually sent in the body of the
+     * block. And I think that if the standard static tree definition
+     * is willing to include Huffman codes that don't correspond to a
+     * symbol, then it's an excessive restriction on dynamic tables
+     * not to permit them to do the same. In particular, it would be
+     * strange for a dynamic block not to be able to exactly mimic
+     * either or both of the Huffman codes used by a static block for
+     * the corresponding alphabet.
+     *
+     * So we place no constraint on HLIT or HDIST during code
+     * construction, and we make this array large enough to include
+     * the maximum number of code lengths that can possibly arise as a
+     * result. It's only trying to _use_ the junk Huffman codes after
+     * table construction is completed that will provoke a decode
+     * error.
+     */
+    unsigned char lengths[288 + 32];
+
     unsigned long bits;
     int nbits;
     unsigned char window[WINSIZE];
@@ -1105,10 +1149,13 @@ bool zlib_decompress_block(ssh_decompressor *dc,
 		    zlib_freetable(&dctx->currdisttable);
 		    dctx->currdisttable = NULL;
 		}
-	    } else if (code < 286) {   /* static tree can give >285; ignore */
+	    } else if (code < 286) {
 		dctx->state = GOTLENSYM;
 		dctx->sym = code;
-	    }
+	    } else {
+                /* literal/length symbols 286 and 287 are invalid */
+                goto decode_error;
+            }
 	    break;
 	  case GOTLENSYM:
 	    rec = &lencodes[dctx->sym - 257];

+ 13 - 6
source/putty/stripctrl.c

@@ -168,16 +168,19 @@ static inline void stripctrl_check_line_limit(
 
 static inline void stripctrl_locale_put_wc(StripCtrlCharsImpl *scc, wchar_t wc)
 {
-    if (iswprint(wc) || stripctrl_ctrlchar_ok(scc, wc)) {
+    int width = mk_wcwidth(wc);
+    if ((iswprint(wc) && width >= 0) || stripctrl_ctrlchar_ok(scc, wc)) {
         /* Printable character, or one we're going to let through anyway. */
     } else if (scc->substitution) {
         wc = scc->substitution;
+        width = mk_wcwidth(wc);
+        assert(width >= 0);
     } else {
         /* No defined substitution, so don't write any output wchar_t. */
         return;
     }
 
-    stripctrl_check_line_limit(scc, wc, mk_wcwidth(wc));
+    stripctrl_check_line_limit(scc, wc, width);
 
     char outbuf[MB_LEN_MAX];
     size_t produced = wcrtomb(outbuf, wc, &scc->mbs_out);
@@ -189,15 +192,19 @@ static inline void stripctrl_term_put_wc(
     StripCtrlCharsImpl *scc, unsigned long wc)
 {
     ptrlen prefix = PTRLEN_LITERAL("");
+    int width = term_char_width(scc->term, wc);
 
-    if (!(wc & ~0x9F)) {
+    if (!(wc & ~0x9F) || width < 0) {
         /* This is something the terminal interprets as a control
          * character. */
         if (!stripctrl_ctrlchar_ok(scc, wc)) {
-            if (!scc->substitution)
+            if (!scc->substitution) {
                 return;
-            else
+            } else {
                 wc = scc->substitution;
+                width = term_char_width(scc->term, wc);
+                assert(width >= 0);
+            }
         }
 
         if (wc == '\012') {
@@ -209,7 +216,7 @@ static inline void stripctrl_term_put_wc(
         }
     }
 
-    stripctrl_check_line_limit(scc, wc, term_char_width(scc->term, wc));
+    stripctrl_check_line_limit(scc, wc, width);
 
     if (prefix.len)
         put_datapl(scc->bs_out, prefix);

+ 40 - 1
source/putty/utils.c

@@ -308,7 +308,10 @@ int string_length_for_printf(size_t s)
 #endif
 
 /* Also lack of vsnprintf before VS2015 */
-#if defined _WINDOWS && !defined __WINE__ && _MSC_VER < 1900
+#if defined _WINDOWS && \
+    !defined __MINGW32__ && \
+    !defined __WINE__ && \
+    _MSC_VER < 1900
 #define vsnprintf _vsnprintf
 #endif
 
@@ -512,6 +515,20 @@ char *fgetline(FILE *fp)
     return ret;
 }
 
+/*
+ * Read an entire file into a BinarySink.
+ */
+bool read_file_into(BinarySink *bs, FILE *fp)
+{
+    char buf[4096];
+    while (1) {
+        size_t retd = fread(buf, 1, sizeof(buf), fp);
+        if (retd == 0)
+            return !ferror(fp);
+        put_data(bs, buf, retd);
+    }
+}
+
 /*
  * Perl-style 'chomp', for a line we just read with fgetline. Unlike
  * Perl chomp, however, we're deliberately forgiving of strange
@@ -952,6 +969,28 @@ bool ptrlen_endswith(ptrlen whole, ptrlen suffix, ptrlen *tail)
     return false;
 }
 
+ptrlen ptrlen_get_word(ptrlen *input, const char *separators)
+{
+    const char *p = input->ptr, *end = p + input->len;
+    ptrlen toret;
+
+    while (p < end && strchr(separators, *p))
+        p++;
+    toret.ptr = p;
+    while (p < end && !strchr(separators, *p))
+        p++;
+    toret.len = p - (const char *)toret.ptr;
+
+    { // WINSCP
+    size_t to_consume = p - (const char *)input->ptr;
+    assert(to_consume <= input->len);
+    input->ptr = (const char *)input->ptr + to_consume;
+    input->len -= to_consume;
+    } // WINSCP
+
+    return toret;
+}
+
 char *mkstr(ptrlen pl)
 {
     char *p = snewn(pl.len + 1, char);

+ 5 - 5
source/putty/version.h

@@ -1,6 +1,6 @@
 /* Generated by automated build script */
-#define RELEASE 0.71
-#define TEXTVER "Release 0.71"
-#define SSHVER "-Release-0.71"
-#define BINARY_VERSION 0,71,0,0
-#define SOURCE_COMMIT "abfc751c3ee7d57bf3f127a458c40bb4ca2b6996"
+#define RELEASE 0.72
+#define TEXTVER "Release 0.72"
+#define SSHVER "-Release-0.72"
+#define BINARY_VERSION 0,72,0,0
+#define SOURCE_COMMIT "75cd6c8b2703137e574223d90d2f3ead9ca34acc"

+ 4 - 0
source/putty/windows/wingss.c

@@ -173,6 +173,8 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf, LogContext *logctx) // MPEXT
         BIND_GSS_FN(release_buffer);
         BIND_GSS_FN(release_cred);
         BIND_GSS_FN(release_name);
+        BIND_GSS_FN(acquire_cred);
+        BIND_GSS_FN(inquire_cred_by_mech);
 
 #undef BIND_GSS_FN
 
@@ -263,6 +265,8 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf, LogContext *logctx) // MPEXT
         BIND_GSS_FN(release_buffer);
         BIND_GSS_FN(release_cred);
         BIND_GSS_FN(release_name);
+        BIND_GSS_FN(acquire_cred);
+        BIND_GSS_FN(inquire_cred_by_mech);
 
 #undef BIND_GSS_FN
 

+ 2 - 0
source/putty/windows/winhsock.c

@@ -147,6 +147,8 @@ static void sk_handle_close(Socket *s)
     }
 #endif
 
+    delete_callbacks_for_context(get_callback_set(hs->plug), hs);
+
     sfree(hs);
 }
 

+ 3 - 6
source/putty/windows/winnet.c

@@ -258,9 +258,7 @@ void sk_init(void)
     if (GetProcAddress(winsock_module, "getaddrinfo") != NULL) {
 	GET_WINDOWS_FUNCTION(winsock_module, getaddrinfo);
 	GET_WINDOWS_FUNCTION(winsock_module, freeaddrinfo);
-#pragma option push -w-cpt
-	GET_WINDOWS_FUNCTION(winsock_module, getnameinfo);
-#pragma option pop
+	GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, getnameinfo);
         /* This function would fail its type-check if we did one,
          * because the VS header file provides an inline definition
          * which is __cdecl instead of WINAPI. */
@@ -271,10 +269,8 @@ void sk_init(void)
 	if (wship6_module) {
 	    GET_WINDOWS_FUNCTION(wship6_module, getaddrinfo);
 	    GET_WINDOWS_FUNCTION(wship6_module, freeaddrinfo);
-#pragma option push -w-cpt
-	    GET_WINDOWS_FUNCTION(wship6_module, getnameinfo);
-#pragma option pop
             /* See comment above about type check */
+	    GET_WINDOWS_FUNCTION_NO_TYPECHECK(wship6_module, getnameinfo);
             GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, gai_strerror);
 	} else {
 	}
@@ -1453,6 +1449,7 @@ static void sk_net_close(Socket *sock)
     p_closesocket(s->s);
     if (s->addr)
 	sk_addr_free(s->addr);
+    delete_callbacks_for_context(get_callback_set(s->plug), s);
     sfree(s);
 }
 

+ 0 - 12
source/putty/windows/winnoise.c

@@ -74,18 +74,6 @@ void noise_get_heavy(void (*func) (void *, int))
     read_random_seed(func);
 }
 
-void random_save_seed(void)
-{
-    int len;
-    void *data;
-
-    if (random_active) {
-	random_get_savedata(&data, &len);
-	write_random_seed(data, len);
-	sfree(data);
-    }
-}
-
 /*
  * This function is called on a timer, and it will monitor
  * frequently changing quantities such as the state of physical and

+ 5 - 5
source/putty/windows/winpgntc.c

@@ -109,13 +109,13 @@ agent_pending_query *agent_query(
      */
     id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds);
     if (id > 0) {
-        unsigned int length_field = GET_32BIT_MSB_FIRST(p);
+        uint32_t length_field = GET_32BIT_MSB_FIRST(p);
         if (length_field > 0 && length_field <= AGENT_MAX_MSGLEN - 4) {
-	    retlen = length_field + 4;
-	    ret = snewn(retlen, unsigned char);
+            retlen = length_field + 4;
+            ret = snewn(retlen, unsigned char);
 	    memcpy(ret, p, retlen);
-	    *out = ret;
-	    *outlen = retlen;
+            *out = ret;
+            *outlen = retlen;
         } else {
             /*
              * If we get here, we received an out-of-range length

+ 55 - 40
source/putty/windows/winstore.c

@@ -531,12 +531,17 @@ static bool try_random_seed(char const *path, int action, HANDLE *ret)
     return (*ret != INVALID_HANDLE_VALUE);
 }
 
+static bool try_random_seed_and_free(char *path, int action, HANDLE *hout)
+{
+    bool retd = try_random_seed(path, action, hout);
+    sfree(path);
+    return retd;
+}
+
 static HANDLE access_random_seed(int action)
 {
     HKEY rkey;
-    DWORD type, size;
     HANDLE rethandle;
-    char seedpath[2 * MAX_PATH + 10] = "\0";
 
     /*
      * Iterate over a selection of possible random seed paths until
@@ -553,17 +558,18 @@ static HANDLE access_random_seed(int action)
      * First, try the location specified by the user in the
      * Registry, if any.
      */
-    size = sizeof(seedpath);
-    if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &rkey) ==
-	ERROR_SUCCESS) {
-	int ret = RegQueryValueEx(rkey, "RandSeedFile",
-				  0, &type, (BYTE *)seedpath, &size);
-	if (ret != ERROR_SUCCESS || type != REG_SZ)
-	    seedpath[0] = '\0';
-	RegCloseKey(rkey);
-
-	if (*seedpath && try_random_seed(seedpath, action, &rethandle))
-	    return rethandle;
+    {
+        char regpath[MAX_PATH + 1];
+        DWORD type, size = sizeof(regpath);
+        if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &rkey) ==
+            ERROR_SUCCESS) {
+            int ret = RegQueryValueEx(rkey, "RandSeedFile",
+                                      0, &type, (BYTE *)regpath, &size);
+            RegCloseKey(rkey);
+            if (ret == ERROR_SUCCESS && type == REG_SZ &&
+                try_random_seed(regpath, action, &rethandle))
+                return rethandle;
+        }
     }
 
     /*
@@ -584,19 +590,20 @@ static HANDLE access_random_seed(int action)
 	tried_shgetfolderpath = true;
     }
     if (p_SHGetFolderPathA) {
+        char profile[MAX_PATH + 1];
 	if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA,
-					 NULL, SHGFP_TYPE_CURRENT, seedpath))) {
-	    strcat(seedpath, "\\PUTTY.RND");
-	    if (try_random_seed(seedpath, action, &rethandle))
-		return rethandle;
-	}
+					 NULL, SHGFP_TYPE_CURRENT, profile)) &&
+            try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND",
+                                            (const char *)NULL),
+                                     action, &rethandle))
+            return rethandle;
 
 	if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_APPDATA,
-					 NULL, SHGFP_TYPE_CURRENT, seedpath))) {
-	    strcat(seedpath, "\\PUTTY.RND");
-	    if (try_random_seed(seedpath, action, &rethandle))
-		return rethandle;
-	}
+					 NULL, SHGFP_TYPE_CURRENT, profile)) &&
+            try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND",
+                                            (const char *)NULL),
+                                     action, &rethandle))
+            return rethandle;
     }
 
     /*
@@ -604,28 +611,36 @@ static HANDLE access_random_seed(int action)
      * user's home directory.
      */
     {
-	int len, ret;
-
-	len =
-	    GetEnvironmentVariable("HOMEDRIVE", seedpath,
-				   sizeof(seedpath));
-	ret =
-	    GetEnvironmentVariable("HOMEPATH", seedpath + len,
-				   sizeof(seedpath) - len);
-	if (ret != 0) {
-	    strcat(seedpath, "\\PUTTY.RND");
-	    if (try_random_seed(seedpath, action, &rethandle))
-		return rethandle;
-	}
+	char drv[MAX_PATH], path[MAX_PATH];
+
+        DWORD drvlen = GetEnvironmentVariable("HOMEDRIVE", drv, sizeof(drv));
+        DWORD pathlen = GetEnvironmentVariable("HOMEPATH", path, sizeof(path));
+
+        /* We permit %HOMEDRIVE% to expand to an empty string, but if
+         * %HOMEPATH% does that, we abort the attempt. Same if either
+         * variable overflows its buffer. */
+        if (drvlen == 0)
+            drv[0] = '\0';
+
+        if (drvlen < lenof(drv) && pathlen < lenof(path) && pathlen > 0 &&
+            try_random_seed_and_free(
+                dupcat(drv, path, "\\PUTTY.RND", (const char *)NULL),
+                action, &rethandle))
+            return rethandle;
     }
 
     /*
      * And finally, fall back to C:\WINDOWS.
      */
-    GetWindowsDirectory(seedpath, sizeof(seedpath));
-    strcat(seedpath, "\\PUTTY.RND");
-    if (try_random_seed(seedpath, action, &rethandle))
-	return rethandle;
+    {
+        char windir[MAX_PATH];
+        DWORD len = GetWindowsDirectory(windir, sizeof(windir));
+        if (len < lenof(windir) &&
+            try_random_seed_and_free(
+                dupcat(windir, "\\PUTTY.RND", (const char *)NULL),
+                action, &rethandle))
+            return rethandle;
+    }
 
     /*
      * If even that failed, give up.

+ 18 - 11
source/putty/windows/winucs.c

@@ -1173,21 +1173,28 @@ int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen,
 	    wchar_t ch = wcstr[i];
 	    int by;
 	    char *p1;
-	    if (ucsdata->uni_tbl && (p1 = ucsdata->uni_tbl[(ch >> 8) & 0xFF])
-		&& (by = p1[ch & 0xFF]))
-		*p++ = by;
+
+            #define WRITECH(chr) do             \
+            {                                   \
+                assert(p - mbstr < mblen);      \
+                *p++ = (char)(chr);             \
+            } while (0)
+
+            if (ucsdata->uni_tbl &&
+                (p1 = ucsdata->uni_tbl[(ch >> 8) & 0xFF]) != NULL &&
+		(by = p1[ch & 0xFF]) != '\0')
+                WRITECH(by);
 	    else if (ch < 0x80)
-		*p++ = (char) ch;
-	    else if (defchr) {
-		int j;
-		for (j = 0; defchr[j]; j++)
-		    *p++ = defchr[j];
-	    }
+                WRITECH(ch);
+            else if (defchr)
+                for (const char *q = defchr; *q; q++)
+                    WRITECH(*q);
 #if 1
 	    else
-		*p++ = '.';
+		WRITECH('.');
 #endif
-	    assert(p - mbstr < mblen);
+
+            #undef WRITECH
 	}
 	return p - mbstr;
     } else {