]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
Merge branch 'pre-0.67'
authorSimon Tatham <anakin@pobox.com>
Mon, 29 Feb 2016 19:59:59 +0000 (19:59 +0000)
committerSimon Tatham <anakin@pobox.com>
Mon, 29 Feb 2016 19:59:59 +0000 (19:59 +0000)
1  2 
Buildscr
doc/plink.but
misc.h
pscp.c
ssh.c
windows/winnpc.c
windows/winpgnt.c

diff --combined Buildscr
index 84dda5c77962c387d65ed81dfb6fe0698102eef1,fb228d9c78a080e70a81c93b154fe9f66e479460..b6fa991f7d1131f45a308167a010847a1bbed0f1
+++ b/Buildscr
@@@ -35,7 -35,7 +35,7 @@@ module putt
  ifeq "$(RELEASE)" "" set Ndate $(!builddate)
  ifneq "$(Ndate)" "" in . do echo $(Ndate) | perl -pe 's/(....)(..)(..)/$$1-$$2-$$3/' > date
  ifneq "$(Ndate)" "" read Date date
- set Epoch 15746 # update this at every release
+ set Epoch 15860 # update this at every release
  ifneq "$(Ndate)" "" in . do echo $(Ndate) | perl -ne 'use Time::Local; /(....)(..)(..)/ and print timegm(0,0,0,$$3,$$2-1,$$1) / 86400 - $(Epoch)' > days
  ifneq "$(Ndate)" "" read Days days
  
@@@ -150,7 -150,7 +150,7 @@@ in putty do perl -i~ -pe 'y/\015//d;s/$
  
  delegate windows
    # FIXME: Cygwin alternative?
 -  in putty/windows do/win vcvars32 && nmake -f Makefile.vc $(Makeargs)
 +  in putty/windows do/win vcvars32 && nmake -f Makefile.vc $(Makeargs) all cleantestprogs
    # Code-sign the binaries, if the local bob config provides a script
    # to do so. We assume here that the script accepts an -i option to
    # provide a 'more info' URL, and an optional -n option to provide a
diff --combined doc/plink.but
index 889d3b6acf6973c62e35f05883657fd544ebaeaf,ec5d162c57920c801d0bc03993a693ad919c43b6..cecdcb03d8ac1fe5f07fdd767ed6631ff1bad647
@@@ -41,7 -41,7 +41,7 @@@ use Plink
  
  \c Z:\sysosd>plink
  \c Plink: command-line connection utility
- \c Release 0.66
+ \c Release 0.67
  \c Usage: plink [options] [user@]host [command]
  \c        ("host" can also be a PuTTY saved session name)
  \c Options:
@@@ -80,8 -80,9 +80,9 @@@
  \c   -N        don't start a shell/command (SSH-2 only)
  \c   -nc host:port
  \c             open tunnel in place of session (SSH-2 only)
- \c   -shareexists
- \c             test whether a connection-sharing upstream exists
+ \c   -sshlog file
+ \c   -sshrawlog file
+ \c             log protocol details to a file
  
  Once this works, you are ready to use Plink.
  
@@@ -232,27 -233,6 +233,27 @@@ line
  
  (This option is only meaningful with the SSH-2 protocol.)
  
 +\S2{plink-option-shareexists} \I{-shareexists-plink}\c{-shareexists}:
 +test for connection-sharing upstream
 +
 +This option does not make a new connection; instead it allows testing
 +for the presence of an existing connection that can be shared.
 +(See \k{config-ssh-sharing} for more information about SSH connection
 +sharing.)
 +
 +A Plink invocation of the form:
 +
 +\c plink -shareexists <session>
 +\e                    iiiiiiiii
 +
 +will test whether there is currently a viable \q{upstream} for the
 +session in question, which can be specified using any syntax you'd
 +normally use with Plink to make an actual connection (a host/port
 +number, a bare saved session name, \c{-load}, etc). It returns a
 +zero exit status if a usable \q{upstream} exists, nonzero otherwise.
 +
 +(This option is only meaningful with the SSH-2 protocol.)
 +
  \H{plink-batch} Using Plink in \i{batch files} and \i{scripts}
  
  Once you have set up Plink to be able to log in to a remote server
diff --combined misc.h
index ae33e96e3fff7cb8dfd8d3d6cfc26a5bd5d2370c,db7f9143c58b377ffa4c44f9f647a8b3a8083097..d5999cbedee2f1d723e0fe8755cc6d638791708e
--- 1/misc.h
--- 2/misc.h
+++ b/misc.h
@@@ -51,12 -51,11 +51,12 @@@ wchar_t *dup_mb_to_wc(int codepage, in
  int toint(unsigned);
  
  char *fgetline(FILE *fp);
 +char *chomp(char *str);
  int strstartswith(const char *s, const char *t);
  int strendswith(const char *s, const char *t);
  
 -void base64_encode_atom(unsigned char *data, int n, char *out);
 -int base64_decode_atom(char *atom, unsigned char *out);
 +void base64_encode_atom(const unsigned char *data, int n, char *out);
 +int base64_decode_atom(const char *atom, unsigned char *out);
  
  struct bufchain_granule;
  typedef struct bufchain_tag {
@@@ -90,23 -89,6 +90,23 @@@ void smemclr(void *b, size_t len)
   * by the 'eq' in the name. */
  int smemeq(const void *av, const void *bv, size_t len);
  
 +/* Extracts an SSH-marshalled string from the start of *data. If
 + * successful (*datalen is not too small), advances data/datalen past
 + * the string and returns a pointer to the string itself and its
 + * length in *stringlen. Otherwise does nothing and returns NULL.
 + *
 + * Like strchr, this function can discard const from its parameter.
 + * Treat it as if it was a family of two functions, one returning a
 + * non-const string given a non-const pointer, and one taking and
 + * returning const. */
 +void *get_ssh_string(int *datalen, const void **data, int *stringlen);
 +/* Extracts an SSH uint32, similarly. Returns TRUE on success, and
 + * leaves the extracted value in *ret. */
 +int get_ssh_uint32(int *datalen, const void **data, unsigned *ret);
 +/* Given a not-necessarily-zero-terminated string in (length,data)
 + * form, check if it equals an ordinary C zero-terminated string. */
 +int match_ssh_id(int stringlen, const void *string, const char *id);
 +
  /*
   * Debugging functions.
   *
   */
  
  #ifdef DEBUG
 -void debug_printf(char *fmt, ...);
 -void debug_memdump(void *buf, int len, int L);
 +void debug_printf(const char *fmt, ...);
 +void debug_memdump(const void *buf, int len, int L);
  #define debug(x) (debug_printf x)
  #define dmemdump(buf,len) debug_memdump (buf, len, 0);
  #define dmemdumpl(buf,len) debug_memdump (buf, len, 1);
    (cp)[0] = (unsigned char)((value) >> 8), \
    (cp)[1] = (unsigned char)(value) )
  
+ /* Replace NULL with the empty string, permitting an idiom in which we
+  * get a string (pointer,length) pair that might be NULL,0 and can
+  * then safely say things like printf("%.*s", length, NULLTOEMPTY(ptr)) */
+ #define NULLTOEMPTY(s) ((s)?(s):"")
  #endif
diff --combined pscp.c
index a4e55fe09b457be378152051125797071afe9b60,dc9e1f5018f0e40515308ede348ce8906301cb92..61e6e1af0ef9a215a317d04f93a189e59ccc1965
--- 1/pscp.c
--- 2/pscp.c
+++ b/pscp.c
@@@ -48,9 -48,9 +48,9 @@@ static void *backhandle
  static Conf *conf;
  int sent_eof = FALSE;
  
 -static void source(char *src);
 -static void rsource(char *src);
 -static void sink(char *targ, char *src);
 +static void source(const char *src);
 +static void rsource(const char *src);
 +static void sink(const char *targ, const char *src);
  
  const char *const appname = "PSCP";
  
   */
  #define MAX_SCP_BUFSIZE 16384
  
 -void ldisc_send(void *handle, char *buf, int len, int interactive)
 -{
 -    /*
 -     * This is only here because of the calls to ldisc_send(NULL,
 -     * 0) in ssh.c. Nothing in PSCP actually needs to use the ldisc
 -     * as an ldisc. So if we get called with any real data, I want
 -     * to know about it.
 -     */
 -    assert(len == 0);
 -}
 +void ldisc_echoedit_update(void *handle) { }
  
 -static void tell_char(FILE * stream, char c)
 +static void tell_char(FILE *stream, char c)
  {
      fputc(c, stream);
  }
  
 -static void tell_str(FILE * stream, char *str)
 +static void tell_str(FILE *stream, const char *str)
  {
      unsigned int i;
  
@@@ -75,7 -84,7 +75,7 @@@
        tell_char(stream, str[i]);
  }
  
 -static void tell_user(FILE * stream, char *fmt, ...)
 +static void tell_user(FILE *stream, const char *fmt, ...)
  {
      char *str, *str2;
      va_list ap;
  /*
   *  Print an error message and perform a fatal exit.
   */
 -void fatalbox(char *fmt, ...)
 +void fatalbox(const char *fmt, ...)
  {
      char *str, *str2;
      va_list ap;
  
      cleanup_exit(1);
  }
 -void modalfatalbox(char *fmt, ...)
 +void modalfatalbox(const char *fmt, ...)
  {
      char *str, *str2;
      va_list ap;
  
      cleanup_exit(1);
  }
 -void nonfatal(char *fmt, ...)
 +void nonfatal(const char *fmt, ...)
  {
      char *str, *str2;
      va_list ap;
      sfree(str2);
      errs++;
  }
 -void connection_fatal(void *frontend, char *fmt, ...)
 +void connection_fatal(void *frontend, const char *fmt, ...)
  {
      char *str, *str2;
      va_list ap;
@@@ -302,7 -311,7 +302,7 @@@ static void ssh_scp_init(void
  /*
   *  Print an error message and exit after closing the SSH link.
   */
 -static void bump(char *fmt, ...)
 +static void bump(const char *fmt, ...)
  {
      char *str, *str2;
      va_list ap;
@@@ -533,7 -542,7 +533,7 @@@ static void do_cmd(char *host, char *us
  /*
   *  Update statistic information about current file.
   */
 -static void print_stats(char *name, uint64 size, uint64 done,
 +static void print_stats(const char *name, uint64 size, uint64 done,
                        time_t start, time_t now)
  {
      float ratebs;
@@@ -605,6 -614,30 +605,6 @@@ static char *colon(char *str
        return (NULL);
  }
  
 -/*
 - * Return a pointer to the portion of str that comes after the last
 - * slash (or backslash or colon, if `local' is TRUE).
 - */
 -static char *stripslashes(char *str, int local)
 -{
 -    char *p;
 -
 -    if (local) {
 -        p = strchr(str, ':');
 -        if (p) str = p+1;
 -    }
 -
 -    p = strrchr(str, '/');
 -    if (p) str = p+1;
 -
 -    if (local) {
 -      p = strrchr(str, '\\');
 -      if (p) str = p+1;
 -    }
 -
 -    return str;
 -}
 -
  /*
   * Determine whether a string is entirely composed of dots.
   */
@@@ -668,7 -701,7 +668,7 @@@ static int sftp_ls_compare(const void *
      const struct fxp_name *b = (const struct fxp_name *) bv;
      return strcmp(a->filename, b->filename);
  }
 -void scp_sftp_listdir(char *dirname)
 +void scp_sftp_listdir(const char *dirname)
  {
      struct fxp_handle *dirh;
      struct fxp_names *names;
@@@ -767,7 -800,7 +767,7 @@@ static struct fxp_handle *scp_sftp_file
  static struct fxp_xfer *scp_sftp_xfer;
  static uint64 scp_sftp_fileoffset;
  
 -int scp_source_setup(char *target, int shouldbedir)
 +int scp_source_setup(const char *target, int shouldbedir)
  {
      if (using_sftp) {
        /*
@@@ -833,7 -866,7 +833,7 @@@ int scp_send_filetimes(unsigned long mt
      }
  }
  
 -int scp_send_filename(char *name, uint64 size, int permissions)
 +int scp_send_filename(const char *name, uint64 size, int permissions)
  {
      if (using_sftp) {
        char *fullname;
@@@ -988,7 -1021,7 +988,7 @@@ void scp_restore_remotepath(char *data
        scp_sftp_remotepath = data;
  }
  
 -int scp_send_dirname(char *name, int modes)
 +int scp_send_dirname(const char *name, int modes)
  {
      if (using_sftp) {
        char *fullname;
@@@ -1063,7 -1096,7 +1063,7 @@@ int scp_send_enddir(void
   * right at the start, whereas scp_sink_init is called to
   * initialise every level of recursion in the protocol.
   */
 -int scp_sink_setup(char *source, int preserve, int recursive)
 +int scp_sink_setup(const char *source, int preserve, int recursive)
  {
      if (using_sftp) {
        char *newsource;
@@@ -1495,7 -1528,7 +1495,7 @@@ int scp_get_sink_action(struct scp_sink
        {
            char sizestr[40];
        
-           if (sscanf(act->buf, "%lo %s %n", &act->permissions,
+             if (sscanf(act->buf, "%lo %39s %n", &act->permissions,
                         sizestr, &i) != 2)
                bump("Protocol error: Illegal file descriptor format");
            act->size = uint64_from_decimal(sizestr);
@@@ -1633,12 -1666,12 +1633,12 @@@ static void run_err(const char *fmt, ..
  /*
   *  Execute the source part of the SCP protocol.
   */
 -static void source(char *src)
 +static void source(const char *src)
  {
      uint64 size;
      unsigned long mtime, atime;
      long permissions;
 -    char *last;
 +    const char *last;
      RFile *f;
      int attr;
      uint64 i;
            /*
             * Avoid . and .. directories.
             */
 -          char *p;
 +          const char *p;
            p = strrchr(src, '/');
            if (!p)
                p = strrchr(src, '\\');
  /*
   *  Recursively send the contents of a directory.
   */
 -static void rsource(char *src)
 +static void rsource(const char *src)
  {
 -    char *last;
 +    const char *last;
      char *save_target;
      DirHandle *dir;
  
  /*
   * Execute the sink part of the SCP protocol.
   */
 -static void sink(char *targ, char *src)
 +static void sink(const char *targ, const char *src)
  {
      char *destfname;
      int targisdir = 0;
   */
  static void toremote(int argc, char *argv[])
  {
 -    char *src, *targ, *host, *user;
 +    char *src, *wtarg, *host, *user;
 +    const char *targ;
      char *cmd;
      int i, wc_type;
  
      uploading = 1;
  
 -    targ = argv[argc - 1];
 +    wtarg = argv[argc - 1];
  
      /* Separate host from filename */
 -    host = targ;
 -    targ = colon(targ);
 -    if (targ == NULL)
 -      bump("targ == NULL in toremote()");
 -    *targ++ = '\0';
 -    if (*targ == '\0')
 -      targ = ".";
 +    host = wtarg;
 +    wtarg = colon(wtarg);
 +    if (wtarg == NULL)
 +      bump("wtarg == NULL in toremote()");
 +    *wtarg++ = '\0';
      /* Substitute "." for empty target */
 +    if (*wtarg == '\0')
 +      targ = ".";
 +    else
 +        targ = wtarg;
  
      /* Separate host and username */
      user = host;
   */
  static void tolocal(int argc, char *argv[])
  {
 -    char *src, *targ, *host, *user;
 +    char *wsrc, *host, *user;
 +    const char *src, *targ;
      char *cmd;
  
      uploading = 0;
      if (argc != 2)
        bump("More than one remote source not supported");
  
 -    src = argv[0];
 +    wsrc = argv[0];
      targ = argv[1];
  
      /* Separate host from filename */
 -    host = src;
 -    src = colon(src);
 -    if (src == NULL)
 +    host = wsrc;
 +    wsrc = colon(wsrc);
 +    if (wsrc == NULL)
        bump("Local to local copy not supported");
 -    *src++ = '\0';
 -    if (*src == '\0')
 -      src = ".";
 +    *wsrc++ = '\0';
      /* Substitute "." for empty filename */
 +    if (*wsrc == '\0')
 +      src = ".";
 +    else
 +        src = wsrc;
  
      /* Separate username and hostname */
      user = host;
   */
  static void get_dir_list(int argc, char *argv[])
  {
 -    char *src, *host, *user;
 -    char *cmd, *p, *q;
 +    char *wsrc, *host, *user;
 +    const char *src;
 +    char *cmd, *p;
 +    const char *q;
      char c;
  
 -    src = argv[0];
 +    wsrc = argv[0];
  
      /* Separate host from filename */
 -    host = src;
 -    src = colon(src);
 -    if (src == NULL)
 +    host = wsrc;
 +    wsrc = colon(wsrc);
 +    if (wsrc == NULL)
        bump("Local file listing not supported");
 -    *src++ = '\0';
 -    if (*src == '\0')
 -      src = ".";
 +    *wsrc++ = '\0';
      /* Substitute "." for empty filename */
 +    if (*wsrc == '\0')
 +      src = ".";
 +    else
 +        src = wsrc;
  
      /* Separate username and hostname */
      user = host;
@@@ -2262,7 -2285,7 +2262,7 @@@ void version(void
      cleanup_exit(1);
  }
  
 -void cmdline_error(char *p, ...)
 +void cmdline_error(const char *p, ...)
  {
      va_list ap;
      fprintf(stderr, "pscp: ");
diff --combined ssh.c
index 9429724804485dea33c388c0094a2996c1e50368,e1e94d78ac2e80def91f6b33ea9ba21c1bcf0f6d..da43bf0227d6aa3aff2f18ad8c8e2360075e9a8c
--- 1/ssh.c
--- 2/ssh.c
+++ b/ssh.c
@@@ -32,7 -32,6 +32,7 @@@ typedef enum 
      SSH2_PKTCTX_NOKEX,
      SSH2_PKTCTX_DHGROUP,
      SSH2_PKTCTX_DHGEX,
 +    SSH2_PKTCTX_ECDHKEX,
      SSH2_PKTCTX_RSAKEX
  } Pkt_KCtx;
  typedef enum {
@@@ -189,7 -188,7 +189,7 @@@ static unsigned int ssh_tty_parse_boole
  #define translate(x) if (type == x) return #x
  #define translatek(x,ctx) if (type == x && (pkt_kctx == ctx)) return #x
  #define translatea(x,ctx) if (type == x && (pkt_actx == ctx)) return #x
 -static char *ssh1_pkt_type(int type)
 +static const char *ssh1_pkt_type(int type)
  {
      translate(SSH1_MSG_DISCONNECT);
      translate(SSH1_SMSG_PUBLIC_KEY);
      translate(SSH1_CMSG_AUTH_CCARD_RESPONSE);
      return "unknown";
  }
 -static char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type)
 +static const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx,
 +                                 int type)
  {
      translatea(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,SSH2_PKTCTX_GSSAPI);
      translatea(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,SSH2_PKTCTX_GSSAPI);
      translatek(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX);
      translatek(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX);
      translatek(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX);
 +    translatek(SSH2_MSG_KEX_ECDH_INIT, SSH2_PKTCTX_ECDHKEX);
 +    translatek(SSH2_MSG_KEX_ECDH_REPLY, SSH2_PKTCTX_ECDHKEX);
      translate(SSH2_MSG_USERAUTH_REQUEST);
      translate(SSH2_MSG_USERAUTH_FAILURE);
      translate(SSH2_MSG_USERAUTH_SUCCESS);
@@@ -358,9 -354,9 +358,9 @@@ static void ssh2_pkt_addmp(struct Packe
  static int ssh2_pkt_construct(Ssh, struct Packet *);
  static void ssh2_pkt_send(Ssh, struct Packet *);
  static void ssh2_pkt_send_noqueue(Ssh, struct Packet *);
 -static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
 +static int do_ssh1_login(Ssh ssh, const unsigned char *in, int inlen,
                         struct Packet *pktin);
 -static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 +static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
                             struct Packet *pktin);
  static void ssh2_channel_check_close(struct ssh_channel *c);
  static void ssh_channel_destroy(struct ssh_channel *c);
@@@ -407,11 -403,7 +407,11 @@@ static void ssh2_msg_something_unimplem
  #define OUR_V2_MAXPKT 0x4000UL
  #define OUR_V2_PACKETLIMIT 0x9000UL
  
 -const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };
 +const static struct ssh_signkey *hostkey_algs[] = {
 +    &ssh_ecdsa_ed25519,
 +    &ssh_ecdsa_nistp256, &ssh_ecdsa_nistp384, &ssh_ecdsa_nistp521,
 +    &ssh_rsa, &ssh_dss
 +};
  
  const static struct ssh_mac *macs[] = {
      &ssh_hmac_sha256, &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5
@@@ -688,11 -680,11 +688,11 @@@ struct Packet 
      const char *additional_log_text;
  };
  
 -static void ssh1_protocol(Ssh ssh, void *vin, int inlen,
 +static void ssh1_protocol(Ssh ssh, const void *vin, int inlen,
                          struct Packet *pktin);
 -static void ssh2_protocol(Ssh ssh, void *vin, int inlen,
 +static void ssh2_protocol(Ssh ssh, const void *vin, int inlen,
                          struct Packet *pktin);
 -static void ssh2_bare_connection_protocol(Ssh ssh, void *vin, int inlen,
 +static void ssh2_bare_connection_protocol(Ssh ssh, const void *vin, int inlen,
                                            struct Packet *pktin);
  static void ssh1_protocol_setup(Ssh ssh);
  static void ssh2_protocol_setup(Ssh ssh);
@@@ -700,8 -692,7 +700,8 @@@ static void ssh2_bare_connection_protoc
  static void ssh_size(void *handle, int width, int height);
  static void ssh_special(void *handle, Telnet_Special);
  static int ssh2_try_send(struct ssh_channel *c);
 -static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, int len);
 +static void ssh2_add_channel_data(struct ssh_channel *c,
 +                                  const char *buf, int len);
  static void ssh_throttle_all(Ssh ssh, int enable, int bufsize);
  static void ssh2_set_window(struct ssh_channel *c, int newwin);
  static int ssh_sendbuffer(void *handle);
@@@ -710,7 -701,7 +710,7 @@@ static unsigned long ssh_pkt_getuint32(
  static int ssh2_pkt_getbool(struct Packet *pkt);
  static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length);
  static void ssh2_timer(void *ctx, unsigned long now);
 -static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
 +static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen,
                              struct Packet *pktin);
  static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin);
  
@@@ -772,7 -763,6 +772,7 @@@ struct ssh_tag 
      const struct ssh2_cipher *cscipher, *sccipher;
      void *cs_cipher_ctx, *sc_cipher_ctx;
      const struct ssh_mac *csmac, *scmac;
 +    int csmac_etm, scmac_etm;
      void *cs_mac_ctx, *sc_mac_ctx;
      const struct ssh_compress *cscomp, *sccomp;
      void *cs_comp_ctx, *sc_comp_ctx;
      int send_ok;
      int echoing, editing;
  
 +    int session_started;
      void *frontend;
  
      int ospeed, ispeed;                      /* temporaries */
      /* SSH-1 and SSH-2 use this for different things, but both use it */
      int protocol_initial_phase_done;
  
 -    void (*protocol) (Ssh ssh, void *vin, int inlen,
 +    void (*protocol) (Ssh ssh, const void *vin, int inlen,
                      struct Packet *pkt);
 -    struct Packet *(*s_rdpkt) (Ssh ssh, unsigned char **data, int *datalen);
 +    struct Packet *(*s_rdpkt) (Ssh ssh, const unsigned char **data,
 +                               int *datalen);
      int (*do_ssh_init)(Ssh ssh, unsigned char c);
  
      /*
      unsigned long max_data_size;
      int kex_in_progress;
      unsigned long next_rekey, last_rekey;
 -    char *deferred_rekey_reason;    /* points to STATIC string; don't free */
 +    const char *deferred_rekey_reason;
  
      /*
       * Fully qualified host name, which we need if doing GSSAPI.
@@@ -1299,8 -1287,7 +1299,8 @@@ static void ssh1_log_outgoing_packet(Ss
   * Update the *data and *datalen variables.
   * Return a Packet structure when a packet is completed.
   */
 -static struct Packet *ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
 +static struct Packet *ssh1_rdpkt(Ssh ssh, const unsigned char **data,
 +                                 int *datalen)
  {
      struct rdpkt1_state_tag *st = &ssh->rdpkt1_state;
  
@@@ -1555,8 -1542,7 +1555,8 @@@ static void ssh2_log_outgoing_packet(Ss
      pkt->length += (pkt->body - pkt->data);
  }
  
 -static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
 +static struct Packet *ssh2_rdpkt(Ssh ssh, const unsigned char **data,
 +                                 int *datalen)
  {
      struct rdpkt2_state_tag *st = &ssh->rdpkt2_state;
  
      st->maclen = ssh->scmac ? ssh->scmac->len : 0;
  
      if (ssh->sccipher && (ssh->sccipher->flags & SSH_CIPHER_IS_CBC) &&
 -      ssh->scmac) {
 +      ssh->scmac && !ssh->scmac_etm) {
        /*
         * When dealing with a CBC-mode cipher, we want to avoid the
         * possibility of an attacker's tweaking the ciphertext stream
         * length, so we just read data and check the MAC repeatedly,
         * and when the MAC passes, see if the length we've got is
         * plausible.
 +         *
 +         * This defence is unnecessary in OpenSSH ETM mode, because
 +         * the whole point of ETM mode is that the attacker can't
 +         * tweak the ciphertext stream at all without the MAC
 +         * detecting it before we decrypt anything.
         */
  
        /* May as well allocate the whole lot now. */
        st->pktin->data = sresize(st->pktin->data,
                                  st->pktin->maxlen + APIEXTRA,
                                  unsigned char);
 +    } else if (ssh->scmac && ssh->scmac_etm) {
 +      st->pktin->data = snewn(4 + APIEXTRA, unsigned char);
 +
 +        /*
 +         * OpenSSH encrypt-then-MAC mode: the packet length is
 +         * unencrypted, unless the cipher supports length encryption.
 +         */
 +      for (st->i = st->len = 0; st->i < 4; st->i++) {
 +          while ((*datalen) == 0)
 +              crReturn(NULL);
 +          st->pktin->data[st->i] = *(*data)++;
 +          (*datalen)--;
 +      }
 +        /* Cipher supports length decryption, so do it */
 +        if (ssh->sccipher && (ssh->sccipher->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
 +            /* Keep the packet the same though, so the MAC passes */
 +            unsigned char len[4];
 +            memcpy(len, st->pktin->data, 4);
 +            ssh->sccipher->decrypt_length(ssh->sc_cipher_ctx, len, 4, st->incoming_sequence);
 +            st->len = toint(GET_32BIT(len));
 +        } else {
 +            st->len = toint(GET_32BIT(st->pktin->data));
 +        }
 +
 +      /*
 +       * _Completely_ silly lengths should be stomped on before they
 +       * do us any more damage.
 +       */
 +      if (st->len < 0 || st->len > OUR_V2_PACKETLIMIT ||
 +          st->len % st->cipherblk != 0) {
 +          bombout(("Incoming packet length field was garbled"));
 +          ssh_free_packet(st->pktin);
 +          crStop(NULL);
 +      }
 +
 +      /*
 +       * So now we can work out the total packet length.
 +       */
 +      st->packetlen = st->len + 4;
 +
 +      /*
 +       * Allocate memory for the rest of the packet.
 +       */
 +      st->pktin->maxlen = st->packetlen + st->maclen;
 +      st->pktin->data = sresize(st->pktin->data,
 +                                st->pktin->maxlen + APIEXTRA,
 +                                unsigned char);
 +
 +      /*
 +       * Read the remainder of the packet.
 +       */
 +      for (st->i = 4; st->i < st->packetlen + st->maclen; st->i++) {
 +          while ((*datalen) == 0)
 +              crReturn(NULL);
 +          st->pktin->data[st->i] = *(*data)++;
 +          (*datalen)--;
 +      }
 +
 +      /*
 +       * Check the MAC.
 +       */
 +      if (ssh->scmac
 +          && !ssh->scmac->verify(ssh->sc_mac_ctx, st->pktin->data,
 +                                 st->len + 4, st->incoming_sequence)) {
 +          bombout(("Incorrect MAC received on packet"));
 +          ssh_free_packet(st->pktin);
 +          crStop(NULL);
 +      }
 +
 +      /* Decrypt everything between the length field and the MAC. */
 +      if (ssh->sccipher)
 +          ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
 +                                 st->pktin->data + 4,
 +                                 st->packetlen - 4);
      } else {
        st->pktin->data = snewn(st->cipherblk + APIEXTRA, unsigned char);
  
      crFinish(st->pktin);
  }
  
 -static struct Packet *ssh2_bare_connection_rdpkt(Ssh ssh, unsigned char **data,
 +static struct Packet *ssh2_bare_connection_rdpkt(Ssh ssh,
 +                                                 const unsigned char **data,
                                                   int *datalen)
  {
      struct rdpkt2_bare_state_tag *st = &ssh->rdpkt2_bare_state;
@@@ -2076,7 -1982,7 +2076,7 @@@ static void defer_packet(Ssh ssh, int p
      s_wrpkt_defer(ssh, pkt);
  }
  
 -static int ssh_versioncmp(char *a, char *b)
 +static int ssh_versioncmp(const char *a, const char *b)
  {
      char *ae, *be;
      unsigned long av, bv;
@@@ -2153,16 -2059,17 +2153,16 @@@ static void ssh_pkt_addstring_start(str
      ssh_pkt_adduint32(pkt, 0);
      pkt->savedpos = pkt->length;
  }
 -static void ssh_pkt_addstring_str(struct Packet *pkt, const char *data)
 -{
 -    ssh_pkt_adddata(pkt, data, strlen(data));
 -    PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
 -}
  static void ssh_pkt_addstring_data(struct Packet *pkt, const char *data,
                                     int len)
  {
      ssh_pkt_adddata(pkt, data, len);
      PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
  }
 +static void ssh_pkt_addstring_str(struct Packet *pkt, const char *data)
 +{
 +  ssh_pkt_addstring_data(pkt, data, strlen(data));
 +}
  static void ssh_pkt_addstring(struct Packet *pkt, const char *data)
  {
      ssh_pkt_addstring_start(pkt);
@@@ -2243,7 -2150,7 +2243,7 @@@ static struct Packet *ssh2_pkt_init(in
   */
  static int ssh2_pkt_construct(Ssh ssh, struct Packet *pkt)
  {
 -    int cipherblk, maclen, padding, i;
 +    int cipherblk, maclen, padding, unencrypted_prefix, i;
  
      if (ssh->logctx)
          ssh2_log_outgoing_packet(ssh, pkt);
      cipherblk = ssh->cscipher ? ssh->cscipher->blksize : 8;  /* block size */
      cipherblk = cipherblk < 8 ? 8 : cipherblk;        /* or 8 if blksize < 8 */
      padding = 4;
 +    unencrypted_prefix = (ssh->csmac && ssh->csmac_etm) ? 4 : 0;
      if (pkt->length + padding < pkt->forcepad)
        padding = pkt->forcepad - pkt->length;
      padding +=
 -      (cipherblk - (pkt->length + padding) % cipherblk) % cipherblk;
 +      (cipherblk - (pkt->length - unencrypted_prefix + padding) % cipherblk)
 +        % cipherblk;
      assert(padding <= 255);
      maclen = ssh->csmac ? ssh->csmac->len : 0;
      ssh2_pkt_ensure(pkt, pkt->length + padding + maclen);
      for (i = 0; i < padding; i++)
        pkt->data[pkt->length + i] = random_byte();
      PUT_32BIT(pkt->data, pkt->length + padding - 4);
 -    if (ssh->csmac)
 -      ssh->csmac->generate(ssh->cs_mac_ctx, pkt->data,
 -                           pkt->length + padding,
 -                           ssh->v2_outgoing_sequence);
 -    ssh->v2_outgoing_sequence++;       /* whether or not we MACed */
  
 -    if (ssh->cscipher)
 -      ssh->cscipher->encrypt(ssh->cs_cipher_ctx,
 -                             pkt->data, pkt->length + padding);
 +    /* Encrypt length if the scheme requires it */
 +    if (ssh->cscipher && (ssh->cscipher->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
 +        ssh->cscipher->encrypt_length(ssh->cs_cipher_ctx, pkt->data, 4,
 +                                      ssh->v2_outgoing_sequence);
 +    }
  
 +    if (ssh->csmac && ssh->csmac_etm) {
 +        /*
 +         * OpenSSH-defined encrypt-then-MAC protocol.
 +         */
 +        if (ssh->cscipher)
 +            ssh->cscipher->encrypt(ssh->cs_cipher_ctx,
 +                                   pkt->data + 4, pkt->length + padding - 4);
 +        ssh->csmac->generate(ssh->cs_mac_ctx, pkt->data,
 +                             pkt->length + padding,
 +                             ssh->v2_outgoing_sequence);
 +    } else {
 +        /*
 +         * SSH-2 standard protocol.
 +         */
 +        if (ssh->csmac)
 +            ssh->csmac->generate(ssh->cs_mac_ctx, pkt->data,
 +                                 pkt->length + padding,
 +                                 ssh->v2_outgoing_sequence);
 +        if (ssh->cscipher)
 +            ssh->cscipher->encrypt(ssh->cs_cipher_ctx,
 +                                   pkt->data, pkt->length + padding);
 +    }
 +
 +    ssh->v2_outgoing_sequence++;       /* whether or not we MACed */
      pkt->encrypted_len = pkt->length + padding;
  
      /* Ready-to-send packet starts at pkt->data. We return length. */
@@@ -2660,7 -2544,7 +2660,7 @@@ static void *ssh_pkt_getdata(struct Pac
      return pkt->body + (pkt->savedpos - length);
  }
  static int ssh1_pkt_getrsakey(struct Packet *pkt, struct RSAKey *key,
 -                            unsigned char **keystr)
 +                            const unsigned char **keystr)
  {
      int j;
  
@@@ -2941,8 -2825,7 +2941,8 @@@ static void ssh_detect_bugs(Ssh ssh, ch
         (wc_match("OpenSSH_2.[235]*", imp)))) {
        /*
         * These versions only support the original (pre-RFC4419)
 -       * SSH-2 GEX request.
 +       * SSH-2 GEX request, and disconnect with a protocol error if
 +       * we use the newer version.
         */
        ssh->remote_bugs |= BUG_SSH2_OLDGEX;
        logevent("We believe remote version has outdated SSH-2 GEX");
@@@ -3018,10 -2901,6 +3018,10 @@@ static void ssh_send_verstring(Ssh ssh
      }
  
      ssh_fix_verstring(verstring + strlen(protoname));
 +#ifdef FUZZING
 +    /* FUZZING make PuTTY insecure, so make live use difficult. */
 +    verstring[0] = 'I';
 +#endif
  
      if (ssh->version == 2) {
        size_t len;
@@@ -3071,8 -2950,6 +3071,8 @@@ static int do_ssh_init(Ssh ssh, unsigne
        crReturn(1);
      }
  
 +    ssh->session_started = TRUE;
 +
      s->vstrsize = sizeof(protoname) + 16;
      s->vstring = snewn(s->vstrsize, char);
      strcpy(s->vstring, protoname);
@@@ -3282,7 -3159,7 +3282,7 @@@ static int do_ssh_connection_init(Ssh s
  }
  
  static void ssh_process_incoming_data(Ssh ssh,
 -                                    unsigned char **data, int *datalen)
 +                                    const unsigned char **data, int *datalen)
  {
      struct Packet *pktin;
  
  }
  
  static void ssh_queue_incoming_data(Ssh ssh,
 -                                  unsigned char **data, int *datalen)
 +                                  const unsigned char **data, int *datalen)
  {
      bufchain_add(&ssh->queued_incoming_data, *data, *datalen);
      *data += *datalen;
  static void ssh_process_queued_incoming_data(Ssh ssh)
  {
      void *vdata;
 -    unsigned char *data;
 +    const unsigned char *data;
      int len, origlen;
  
      while (!ssh->frozen && bufchain_size(&ssh->queued_incoming_data)) {
@@@ -3327,7 -3204,7 +3327,7 @@@ static void ssh_set_frozen(Ssh ssh, in
      ssh->frozen = frozen;
  }
  
 -static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
 +static void ssh_gotdata(Ssh ssh, const unsigned char *data, int datalen)
  {
      /* Log raw data, if we're in that mode. */
      if (ssh->logctx)
@@@ -3455,20 -3332,34 +3455,20 @@@ static void ssh_socket_log(Plug plug, i
                             const char *error_msg, int error_code)
  {
      Ssh ssh = (Ssh) plug;
 -    char addrbuf[256], *msg;
  
 -    if (ssh->attempting_connshare) {
 -        /*
 -         * While we're attempting connection sharing, don't loudly log
 -         * everything that happens. Real TCP connections need to be
 -         * logged when we _start_ trying to connect, because it might
 -         * be ages before they respond if something goes wrong; but
 -         * connection sharing is local and quick to respond, and it's
 -         * sufficient to simply wait and see whether it worked
 -         * afterwards.
 -         */
 -    } else {
 -        sk_getaddr(addr, addrbuf, lenof(addrbuf));
 -
 -        if (type == 0) {
 -            if (sk_addr_needs_port(addr)) {
 -                msg = dupprintf("Connecting to %s port %d", addrbuf, port);
 -            } else {
 -                msg = dupprintf("Connecting to %s", addrbuf);
 -            }
 -        } else {
 -            msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);
 -        }
 +    /*
 +     * While we're attempting connection sharing, don't loudly log
 +     * everything that happens. Real TCP connections need to be logged
 +     * when we _start_ trying to connect, because it might be ages
 +     * before they respond if something goes wrong; but connection
 +     * sharing is local and quick to respond, and it's sufficient to
 +     * simply wait and see whether it worked afterwards.
 +     */
  
 -        logevent(msg);
 -        sfree(msg);
 -    }
 +    if (!ssh->attempting_connshare)
 +        backend_socket_log(ssh->frontend, type, addr, port,
 +                           error_msg, error_code, ssh->conf,
 +                           ssh->session_started);
  }
  
  void ssh_connshare_log(Ssh ssh, int event, const char *logtext,
@@@ -3552,20 -3443,35 +3552,20 @@@ static void ssh_sent(Plug plug, int buf
        ssh_throttle_all(ssh, 0, bufsize);
  }
  
 -/*
 - * Connect to specified host and port.
 - * Returns an error message, or NULL on success.
 - * Also places the canonical host name into `realhost'. It must be
 - * freed by the caller.
 - */
 -static const char *connect_to_host(Ssh ssh, char *host, int port,
 -                                 char **realhost, int nodelay, int keepalive)
 +static void ssh_hostport_setup(const char *host, int port, Conf *conf,
 +                               char **savedhost, int *savedport,
 +                               char **loghost_ret)
  {
 -    static const struct plug_function_table fn_table = {
 -      ssh_socket_log,
 -      ssh_closing,
 -      ssh_receive,
 -      ssh_sent,
 -      NULL
 -    };
 +    char *loghost = conf_get_str(conf, CONF_loghost);
 +    if (loghost_ret)
 +        *loghost_ret = loghost;
  
 -    SockAddr addr;
 -    const char *err;
 -    char *loghost;
 -    int addressfamily, sshprot;
 -    
 -    loghost = conf_get_str(ssh->conf, CONF_loghost);
      if (*loghost) {
        char *tmphost;
          char *colon;
  
          tmphost = dupstr(loghost);
 -      ssh->savedport = 22;           /* default ssh port */
 +      *savedport = 22;               /* default ssh port */
  
        /*
         * A colon suffix on the hostname string also lets us affect
        if (colon && colon == host_strchr(tmphost, ':')) {
            *colon++ = '\0';
            if (*colon)
 -              ssh->savedport = atoi(colon);
 +              *savedport = atoi(colon);
        }
  
 -        ssh->savedhost = host_strduptrim(tmphost);
 +        *savedhost = host_strduptrim(tmphost);
          sfree(tmphost);
      } else {
 -      ssh->savedhost = host_strduptrim(host);
 +      *savedhost = host_strduptrim(host);
        if (port < 0)
            port = 22;                 /* default ssh port */
 -      ssh->savedport = port;
 +      *savedport = port;
      }
 +}
 +
 +static int ssh_test_for_upstream(const char *host, int port, Conf *conf)
 +{
 +    char *savedhost;
 +    int savedport;
 +    int ret;
 +
 +    random_ref(); /* platform may need this to determine share socket name */
 +    ssh_hostport_setup(host, port, conf, &savedhost, &savedport, NULL);
 +    ret = ssh_share_test_for_upstream(savedhost, savedport, conf);
 +    sfree(savedhost);
 +    random_unref();
 +
 +    return ret;
 +}
 +
 +/*
 + * Connect to specified host and port.
 + * Returns an error message, or NULL on success.
 + * Also places the canonical host name into `realhost'. It must be
 + * freed by the caller.
 + */
 +static const char *connect_to_host(Ssh ssh, const char *host, int port,
 +                                 char **realhost, int nodelay, int keepalive)
 +{
 +    static const struct plug_function_table fn_table = {
 +      ssh_socket_log,
 +      ssh_closing,
 +      ssh_receive,
 +      ssh_sent,
 +      NULL
 +    };
 +
 +    SockAddr addr;
 +    const char *err;
 +    char *loghost;
 +    int addressfamily, sshprot;
 +
 +    ssh_hostport_setup(host, port, ssh->conf,
 +                       &ssh->savedhost, &ssh->savedport, &loghost);
  
      ssh->fn = &fn_table;               /* make 'ssh' usable as a Plug */
  
           * Try to find host.
           */
          addressfamily = conf_get_int(ssh->conf, CONF_addressfamily);
 -        logeventf(ssh, "Looking up host \"%s\"%s", host,
 -                  (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
 -                   (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : "")));
 -        addr = name_lookup(host, port, realhost, ssh->conf, addressfamily);
 +        addr = name_lookup(host, port, realhost, ssh->conf, addressfamily,
 +                           ssh->frontend, "SSH connection");
          if ((err = sk_addr_error(addr)) != NULL) {
              sk_addr_free(addr);
              return err;
@@@ -3789,7 -3656,7 +3789,7 @@@ static void ssh_agentf_callback(void *c
  {
      struct ssh_channel *c = (struct ssh_channel *)cv;
      Ssh ssh = c->ssh;
 -    void *sentreply = reply;
 +    const void *sentreply = reply;
  
      c->u.a.outstanding_requests--;
      if (!sentreply) {
   * non-NULL, otherwise just close the connection. `client_reason' == NULL
   * => log `wire_reason'.
   */
 -static void ssh_disconnect(Ssh ssh, char *client_reason, char *wire_reason,
 +static void ssh_disconnect(Ssh ssh, const char *client_reason,
 +                           const char *wire_reason,
                           int code, int clean_exit)
  {
      char *error;
@@@ -3907,7 -3773,7 +3907,7 @@@ int verify_ssh_manual_host_key(Ssh ssh
  /*
   * Handle the key exchange and user authentication phases.
   */
 -static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
 +static int do_ssh1_login(Ssh ssh, const unsigned char *in, int inlen,
                         struct Packet *pktin)
  {
      int i, j, ret;
      struct do_ssh1_login_state {
        int crLine;
        int len;
 -      unsigned char *rsabuf, *keystr1, *keystr2;
 +      unsigned char *rsabuf;
 +        const unsigned char *keystr1, *keystr2;
        unsigned long supported_ciphers_mask, supported_auths_mask;
        int tried_publickey, tried_agent;
        int tis_auth_refused, ccard_auth_refused;
        void *publickey_blob;
        int publickey_bloblen;
        char *publickey_comment;
 -      int publickey_encrypted;
 +      int privatekey_available, privatekey_encrypted;
        prompts_t *cur_prompt;
        char c;
        int pwpkt_type;
                                              "rsa", keystr, fingerprint,
                                              ssh_dialog_callback, ssh);
              sfree(keystr);
 +#ifdef FUZZING
 +          s->dlgret = 1;
 +#endif
              if (s->dlgret < 0) {
                  do {
                      crReturn(0);
  
      {
        int cipher_chosen = 0, warn = 0;
 -      char *cipher_string = NULL;
 +      const char *cipher_string = NULL;
        int i;
        for (i = 0; !cipher_chosen && i < CIPHER_MAX; i++) {
            int next_cipher = conf_get_int_int(ssh->conf,
      s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
      if (!filename_is_null(s->keyfile)) {
        int keytype;
 -      logeventf(ssh, "Reading private key file \"%.150s\"",
 +      logeventf(ssh, "Reading key file \"%.150s\"",
                  filename_to_str(s->keyfile));
        keytype = key_type(s->keyfile);
 -      if (keytype == SSH_KEYTYPE_SSH1) {
 +      if (keytype == SSH_KEYTYPE_SSH1 ||
 +            keytype == SSH_KEYTYPE_SSH1_PUBLIC) {
            const char *error;
            if (rsakey_pubblob(s->keyfile,
                               &s->publickey_blob, &s->publickey_bloblen,
                               &s->publickey_comment, &error)) {
 -              s->publickey_encrypted = rsakey_encrypted(s->keyfile,
 -                                                        NULL);
 +                s->privatekey_available = (keytype == SSH_KEYTYPE_SSH1);
 +                if (!s->privatekey_available)
 +                    logeventf(ssh, "Key file contains public key only");
 +              s->privatekey_encrypted = rsakey_encrypted(s->keyfile,
 +                                                           NULL);
            } else {
                char *msgbuf;
 -              logeventf(ssh, "Unable to load private key (%s)", error);
 -              msgbuf = dupprintf("Unable to load private key file "
 +              logeventf(ssh, "Unable to load key (%s)", error);
 +              msgbuf = dupprintf("Unable to load key file "
                                   "\"%.150s\" (%s)\r\n",
                                   filename_to_str(s->keyfile),
                                   error);
            if (s->authed)
                break;
        }
 -      if (s->publickey_blob && !s->tried_publickey) {
 +      if (s->publickey_blob && s->privatekey_available &&
 +            !s->tried_publickey) {
            /*
             * Try public key authentication with the specified
             * key file.
                 */
                char *passphrase = NULL;    /* only written after crReturn */
                const char *error;
 -              if (!s->publickey_encrypted) {
 +              if (!s->privatekey_encrypted) {
                    if (flags & FLAG_VERBOSE)
                        c_write_str(ssh, "No passphrase required.\r\n");
                    passphrase = NULL;
@@@ -5562,7 -5419,7 +5562,7 @@@ static void ssh1_msg_port_open(Ssh ssh
      ssh_pkt_getstring(pktin, &host, &hostsize);
      port = ssh_pkt_getuint32(pktin);
  
-     pf.dhost = dupprintf("%.*s", hostsize, host);
+     pf.dhost = dupprintf("%.*s", hostsize, NULLTOEMPTY(host));
      pf.dport = port;
      pfp = find234(ssh->rportfwds, &pf, NULL);
  
@@@ -5826,7 -5683,7 +5826,7 @@@ int ssh_agent_forwarding_permitted(Ssh 
      return conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists();
  }
  
 -static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
 +static void do_ssh1_connection(Ssh ssh, const unsigned char *in, int inlen,
                               struct Packet *pktin)
  {
      crBegin(ssh->do_ssh1_connection_crstate);
        ssh_special(ssh, TS_EOF);
  
      if (ssh->ldisc)
 -      ldisc_send(ssh->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */
 +      ldisc_echoedit_update(ssh->ldisc);  /* cause ldisc to notice changes */
      ssh->send_ok = 1;
      ssh->channels = newtree234(ssh_channelcmp);
      while (1) {
@@@ -6045,7 -5902,7 +6045,7 @@@ static void ssh1_msg_debug(Ssh ssh, str
      int msglen;
  
      ssh_pkt_getstring(pktin, &msg, &msglen);
-     logeventf(ssh, "Remote debug message: %.*s", msglen, msg);
+     logeventf(ssh, "Remote debug message: %.*s", msglen, NULLTOEMPTY(msg));
  }
  
  static void ssh1_msg_disconnect(Ssh ssh, struct Packet *pktin)
      int msglen;
  
      ssh_pkt_getstring(pktin, &msg, &msglen);
-     bombout(("Server sent disconnect message:\n\"%.*s\"", msglen, msg));
+     bombout(("Server sent disconnect message:\n\"%.*s\"",
+              msglen, NULLTOEMPTY(msg)));
  }
  
  static void ssh_msg_ignore(Ssh ssh, struct Packet *pktin)
@@@ -6081,10 -5939,10 +6082,10 @@@ static void ssh1_protocol_setup(Ssh ssh
      ssh->packet_dispatch[SSH1_MSG_DEBUG] = ssh1_msg_debug;
  }
  
 -static void ssh1_protocol(Ssh ssh, void *vin, int inlen,
 +static void ssh1_protocol(Ssh ssh, const void *vin, int inlen,
                          struct Packet *pktin)
  {
 -    unsigned char *in=(unsigned char*)vin;
 +    const unsigned char *in = (const unsigned char *)vin;
      if (ssh->state == SSH_STATE_CLOSED)
        return;
  
  }
  
  /*
 - * Utility routine for decoding comma-separated strings in KEXINIT.
 + * Utility routines for decoding comma-separated strings in KEXINIT.
   */
 -static int in_commasep_string(char *needle, char *haystack, int haylen)
 +static int first_in_commasep_string(char const *needle, char const *haystack,
 +                                  int haylen)
  {
      int needlen;
      if (!needle || !haystack)        /* protect against null pointers */
        return 0;
      needlen = strlen(needle);
 -    while (1) {
 -      /*
 -       * Is it at the start of the string?
 -       */
 -      if (haylen >= needlen &&       /* haystack is long enough */
 -          !memcmp(needle, haystack, needlen) &&       /* initial match */
 -          (haylen == needlen || haystack[needlen] == ',')
 -          /* either , or EOS follows */
 -          )
 -          return 1;
 -      /*
 -       * If not, search for the next comma and resume after that.
 -       * If no comma found, terminate.
 -       */
 -      while (haylen > 0 && *haystack != ',')
 -          haylen--, haystack++;
 -      if (haylen == 0)
 -          return 0;
 -      haylen--, haystack++;          /* skip over comma itself */
 -    }
 +
 +    if (haylen >= needlen &&       /* haystack is long enough */
 +      !memcmp(needle, haystack, needlen) &&   /* initial match */
 +      (haylen == needlen || haystack[needlen] == ',')
 +      /* either , or EOS follows */
 +      )
 +      return 1;
 +    return 0;
  }
  
 -/*
 - * Similar routine for checking whether we have the first string in a list.
 - */
 -static int first_in_commasep_string(char *needle, char *haystack, int haylen)
 +static int in_commasep_string(char const *needle, char const *haystack,
 +                            int haylen)
  {
 -    int needlen;
 +    char *p;
 +
      if (!needle || !haystack)        /* protect against null pointers */
        return 0;
 -    needlen = strlen(needle);
      /*
       * Is it at the start of the string?
       */
 -    if (haylen >= needlen &&       /* haystack is long enough */
 -      !memcmp(needle, haystack, needlen) &&   /* initial match */
 -      (haylen == needlen || haystack[needlen] == ',')
 -      /* either , or EOS follows */
 -      )
 +    if (first_in_commasep_string(needle, haystack, haylen))
        return 1;
 -    return 0;
 +    /*
 +     * If not, search for the next comma and resume after that.
 +     * If no comma found, terminate.
 +     */
 +    p = memchr(haystack, ',', haylen);
 +    if (!p) return 0;
 +    /* + 1 to skip over comma */
 +    return in_commasep_string(needle, p + 1, haylen - (p + 1 - haystack));
 +}
 +
 +/*
 + * Add a value to the comma-separated string at the end of the packet.
 + */
 +static void ssh2_pkt_addstring_commasep(struct Packet *pkt, const char *data)
 +{
 +    if (pkt->length - pkt->savedpos > 0)
 +      ssh_pkt_addstring_str(pkt, ",");
 +    ssh_pkt_addstring_str(pkt, data);
  }
  
  
  /*
 - * SSH-2 key creation method.
 - * (Currently assumes 2 lots of any hash are sufficient to generate
 - * keys/IVs for any cipher/MAC. SSH2_MKKEY_ITERS documents this assumption.)
 + * SSH-2 key derivation (RFC 4253 section 7.2).
   */
 -#define SSH2_MKKEY_ITERS (2)
 -static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, char chr,
 -                     unsigned char *keyspace)
 +static unsigned char *ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H,
 +                                 char chr, int keylen)
  {
      const struct ssh_hash *h = ssh->kex->hash;
 -    void *s;
 +    int keylen_padded;
 +    unsigned char *key;
 +    void *s, *s2;
 +
 +    if (keylen == 0)
 +        return NULL;
 +
 +    /* Round up to the next multiple of hash length. */
 +    keylen_padded = ((keylen + h->hlen - 1) / h->hlen) * h->hlen;
 +
 +    key = snewn(keylen_padded, unsigned char);
 +
      /* First hlen bytes. */
      s = h->init();
      if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))
      h->bytes(s, H, h->hlen);
      h->bytes(s, &chr, 1);
      h->bytes(s, ssh->v2_session_id, ssh->v2_session_id_len);
 -    h->final(s, keyspace);
 -    /* Next hlen bytes. */
 -    s = h->init();
 -    if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))
 -      hash_mpint(h, s, K);
 -    h->bytes(s, H, h->hlen);
 -    h->bytes(s, keyspace, h->hlen);
 -    h->final(s, keyspace + h->hlen);
 +    h->final(s, key);
 +
 +    /* Subsequent blocks of hlen bytes. */
 +    if (keylen_padded > h->hlen) {
 +        int offset;
 +
 +        s = h->init();
 +        if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))
 +            hash_mpint(h, s, K);
 +        h->bytes(s, H, h->hlen);
 +
 +        for (offset = h->hlen; offset < keylen_padded; offset += h->hlen) {
 +            h->bytes(s, key + offset - h->hlen, h->hlen);
 +            s2 = h->copy(s);
 +            h->final(s2, key + offset);
 +        }
 +
 +        h->free(s);
 +    }
 +
 +    /* Now clear any extra bytes of key material beyond the length
 +     * we're officially returning, because the caller won't know to
 +     * smemclr those. */
 +    if (keylen_padded > keylen)
 +        smemclr(key + keylen, keylen_padded - keylen);
 +
 +    return key;
 +}
 +
 +/*
 + * Structure for constructing KEXINIT algorithm lists.
 + */
 +#define MAXKEXLIST 16
 +struct kexinit_algorithm {
 +    const char *name;
 +    union {
 +      struct {
 +          const struct ssh_kex *kex;
 +          int warn;
 +      } kex;
 +      const struct ssh_signkey *hostkey;
 +      struct {
 +          const struct ssh2_cipher *cipher;
 +          int warn;
 +      } cipher;
 +      struct {
 +          const struct ssh_mac *mac;
 +          int etm;
 +      } mac;
 +      const struct ssh_compress *comp;
 +    } u;
 +};
 +
 +/*
 + * Find a slot in a KEXINIT algorithm list to use for a new algorithm.
 + * If the algorithm is already in the list, return a pointer to its
 + * entry, otherwise return an entry from the end of the list.
 + * This assumes that every time a particular name is passed in, it
 + * comes from the same string constant.  If this isn't true, this
 + * function may need to be rewritten to use strcmp() instead.
 + */
 +static struct kexinit_algorithm *ssh2_kexinit_addalg(struct kexinit_algorithm
 +                                                   *list, const char *name)
 +{
 +    int i;
 +
 +    for (i = 0; i < MAXKEXLIST; i++)
 +      if (list[i].name == NULL || list[i].name == name) {
 +          list[i].name = name;
 +          return &list[i];
 +      }
 +    assert(!"No space in KEXINIT list");
 +    return NULL;
  }
  
  /*
   * Handle the SSH-2 transport layer.
   */
 -static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
 +static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen,
                             struct Packet *pktin)
  {
 -    unsigned char *in = (unsigned char *)vin;
 +    const unsigned char *in = (const unsigned char *)vin;
 +    enum kexlist {
 +      KEXLIST_KEX, KEXLIST_HOSTKEY, KEXLIST_CSCIPHER, KEXLIST_SCCIPHER,
 +      KEXLIST_CSMAC, KEXLIST_SCMAC, KEXLIST_CSCOMP, KEXLIST_SCCOMP,
 +      NKEXLIST
 +    };
 +    const char * kexlist_descr[NKEXLIST] = {
 +      "key exchange algorithm", "host key algorithm",
 +      "client-to-server cipher", "server-to-client cipher",
 +      "client-to-server MAC", "server-to-client MAC",
 +      "client-to-server compression method",
 +      "server-to-client compression method" };
      struct do_ssh2_transport_state {
        int crLine;
        int nbits, pbits, warn_kex, warn_cscipher, warn_sccipher;
        const struct ssh2_cipher *sccipher_tobe;
        const struct ssh_mac *csmac_tobe;
        const struct ssh_mac *scmac_tobe;
 +        int csmac_etm_tobe, scmac_etm_tobe;
        const struct ssh_compress *cscomp_tobe;
        const struct ssh_compress *sccomp_tobe;
        char *hostkeydata, *sigdata, *rsakeydata, *keystr, *fingerprint;
        int hostkeylen, siglen, rsakeylen;
        void *hkey;                    /* actual host key */
        void *rsakey;                  /* for RSA kex */
 +        void *eckey;                   /* for ECDH kex */
        unsigned char exchange_hash[SSH2_KEX_MAX_HASH_LEN];
        int n_preferred_kex;
        const struct ssh_kexes *preferred_kex[KEX_MAX];
          int dlgret;
        int guessok;
        int ignorepkt;
 +      struct kexinit_algorithm kexlists[NKEXLIST][MAXKEXLIST];
      };
      crState(do_ssh2_transport_state);
  
    begin_key_exchange:
      ssh->pkt_kctx = SSH2_PKTCTX_NOKEX;
      {
 -      int i, j, k, commalist_started;
 +      int i, j, k, warn;
 +      struct kexinit_algorithm *alg;
  
        /*
         * Set up the preferred key exchange. (NULL => warn below here)
                s->preferred_kex[s->n_preferred_kex++] =
                    &ssh_rsa_kex;
                break;
 +              case KEX_ECDH:
 +                s->preferred_kex[s->n_preferred_kex++] =
 +                    &ssh_ecdh_kex;
 +                break;
              case KEX_WARN:
                /* Flag for later. Don't bother if it's the last in
                 * the list. */
              case CIPHER_ARCFOUR:
                s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_arcfour;
                break;
 +              case CIPHER_CHACHA20:
 +                s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_ccp;
 +                break;
              case CIPHER_WARN:
                /* Flag for later. Don't bother if it's the last in
                 * the list. */
         */
        ssh->kex_in_progress = TRUE;
  
 -      /*
 -       * Construct and send our key exchange packet.
 -       */
 -      s->pktout = ssh2_pkt_init(SSH2_MSG_KEXINIT);
 -      for (i = 0; i < 16; i++)
 -          ssh2_pkt_addbyte(s->pktout, (unsigned char) random_byte());
 +      for (i = 0; i < NKEXLIST; i++)
 +          for (j = 0; j < MAXKEXLIST; j++)
 +              s->kexlists[i][j].name = NULL;
        /* List key exchange algorithms. */
 -      ssh2_pkt_addstring_start(s->pktout);
 -      commalist_started = 0;
 +      warn = FALSE;
        for (i = 0; i < s->n_preferred_kex; i++) {
            const struct ssh_kexes *k = s->preferred_kex[i];
 -          if (!k) continue;          /* warning flag */
 -          for (j = 0; j < k->nkexes; j++) {
 -              if (commalist_started)
 -                  ssh2_pkt_addstring_str(s->pktout, ",");
 -              ssh2_pkt_addstring_str(s->pktout, k->list[j]->name);
 -              commalist_started = 1;
 +          if (!k) warn = TRUE;
 +          else for (j = 0; j < k->nkexes; j++) {
 +              alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_KEX],
 +                                        k->list[j]->name);
 +              alg->u.kex.kex = k->list[j];
 +              alg->u.kex.warn = warn;
            }
        }
        /* List server host key algorithms. */
          if (!s->got_session_id) {
              /*
               * In the first key exchange, we list all the algorithms
 -             * we're prepared to cope with.
 +             * we're prepared to cope with, but prefer those algorithms
 +           * for which we have a host key for this host.
               */
 -            ssh2_pkt_addstring_start(s->pktout);
              for (i = 0; i < lenof(hostkey_algs); i++) {
 -                ssh2_pkt_addstring_str(s->pktout, hostkey_algs[i]->name);
 -                if (i < lenof(hostkey_algs) - 1)
 -                    ssh2_pkt_addstring_str(s->pktout, ",");
 -            }
 +              if (have_ssh_host_key(ssh->savedhost, ssh->savedport,
 +                                    hostkey_algs[i]->keytype)) {
 +                  alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
 +                                            hostkey_algs[i]->name);
 +                  alg->u.hostkey = hostkey_algs[i];
 +              }
 +          }
 +            for (i = 0; i < lenof(hostkey_algs); i++) {
 +              alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
 +                                        hostkey_algs[i]->name);
 +              alg->u.hostkey = hostkey_algs[i];
 +          }
          } else {
              /*
               * In subsequent key exchanges, we list only the kex
               * reverification.
               */
              assert(ssh->kex);
 -            ssh2_pkt_addstring(s->pktout, ssh->hostkey->name);
 +          alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
 +                                    ssh->hostkey->name);
 +          alg->u.hostkey = ssh->hostkey;
          }
        /* List encryption algorithms (client->server then server->client). */
 -      for (k = 0; k < 2; k++) {
 -          ssh2_pkt_addstring_start(s->pktout);
 -          commalist_started = 0;
 +      for (k = KEXLIST_CSCIPHER; k <= KEXLIST_SCCIPHER; k++) {
 +          warn = FALSE;
 +#ifdef FUZZING
 +          alg = ssh2_kexinit_addalg(s->kexlists[k], "none");
 +          alg->u.cipher.cipher = NULL;
 +          alg->u.cipher.warn = warn;
 +#endif /* FUZZING */
            for (i = 0; i < s->n_preferred_ciphers; i++) {
                const struct ssh2_ciphers *c = s->preferred_ciphers[i];
 -              if (!c) continue;              /* warning flag */
 -              for (j = 0; j < c->nciphers; j++) {
 -                  if (commalist_started)
 -                      ssh2_pkt_addstring_str(s->pktout, ",");
 -                  ssh2_pkt_addstring_str(s->pktout, c->list[j]->name);
 -                  commalist_started = 1;
 +              if (!c) warn = TRUE;
 +              else for (j = 0; j < c->nciphers; j++) {
 +                  alg = ssh2_kexinit_addalg(s->kexlists[k],
 +                                            c->list[j]->name);
 +                  alg->u.cipher.cipher = c->list[j];
 +                  alg->u.cipher.warn = warn;
                }
            }
        }
        /* List MAC algorithms (client->server then server->client). */
 -      for (j = 0; j < 2; j++) {
 -          ssh2_pkt_addstring_start(s->pktout);
 +      for (j = KEXLIST_CSMAC; j <= KEXLIST_SCMAC; j++) {
 +#ifdef FUZZING
 +          alg = ssh2_kexinit_addalg(s->kexlists[j], "none");
 +          alg->u.mac.mac = NULL;
 +          alg->u.mac.etm = FALSE;
 +#endif /* FUZZING */
            for (i = 0; i < s->nmacs; i++) {
 -              ssh2_pkt_addstring_str(s->pktout, s->maclist[i]->name);
 -              if (i < s->nmacs - 1)
 -                  ssh2_pkt_addstring_str(s->pktout, ",");
 -          }
 +              alg = ssh2_kexinit_addalg(s->kexlists[j], s->maclist[i]->name);
 +              alg->u.mac.mac = s->maclist[i];
 +              alg->u.mac.etm = FALSE;
 +            }
 +          for (i = 0; i < s->nmacs; i++)
 +                /* For each MAC, there may also be an ETM version,
 +                 * which we list second. */
 +                if (s->maclist[i]->etm_name) {
 +                  alg = ssh2_kexinit_addalg(s->kexlists[j],
 +                                            s->maclist[i]->etm_name);
 +                  alg->u.mac.mac = s->maclist[i];
 +                  alg->u.mac.etm = TRUE;
 +              }
        }
        /* List client->server compression algorithms,
         * then server->client compression algorithms. (We use the
         * same set twice.) */
 -      for (j = 0; j < 2; j++) {
 -          ssh2_pkt_addstring_start(s->pktout);
 +      for (j = KEXLIST_CSCOMP; j <= KEXLIST_SCCOMP; j++) {
            assert(lenof(compressions) > 1);
            /* Prefer non-delayed versions */
 -          ssh2_pkt_addstring_str(s->pktout, s->preferred_comp->name);
 +          alg = ssh2_kexinit_addalg(s->kexlists[j], s->preferred_comp->name);
 +          alg->u.comp = s->preferred_comp;
            /* We don't even list delayed versions of algorithms until
             * they're allowed to be used, to avoid a race. See the end of
             * this function. */
            if (s->userauth_succeeded && s->preferred_comp->delayed_name) {
 -              ssh2_pkt_addstring_str(s->pktout, ",");
 -              ssh2_pkt_addstring_str(s->pktout,
 -                                     s->preferred_comp->delayed_name);
 +              alg = ssh2_kexinit_addalg(s->kexlists[j],
 +                                        s->preferred_comp->delayed_name);
 +              alg->u.comp = s->preferred_comp;
            }
            for (i = 0; i < lenof(compressions); i++) {
                const struct ssh_compress *c = compressions[i];
 -              if (c != s->preferred_comp) {
 -                  ssh2_pkt_addstring_str(s->pktout, ",");
 -                  ssh2_pkt_addstring_str(s->pktout, c->name);
 -                  if (s->userauth_succeeded && c->delayed_name) {
 -                      ssh2_pkt_addstring_str(s->pktout, ",");
 -                      ssh2_pkt_addstring_str(s->pktout, c->delayed_name);
 -                  }
 +              alg = ssh2_kexinit_addalg(s->kexlists[j], c->name);
 +              alg->u.comp = c;
 +              if (s->userauth_succeeded && c->delayed_name) {
 +                  alg = ssh2_kexinit_addalg(s->kexlists[j], c->delayed_name);
 +                  alg->u.comp = c;
                }
            }
        }
 +      /*
 +       * Construct and send our key exchange packet.
 +       */
 +      s->pktout = ssh2_pkt_init(SSH2_MSG_KEXINIT);
 +      for (i = 0; i < 16; i++)
 +          ssh2_pkt_addbyte(s->pktout, (unsigned char) random_byte());
 +      for (i = 0; i < NKEXLIST; i++) {
 +          ssh2_pkt_addstring_start(s->pktout);
 +          for (j = 0; j < MAXKEXLIST; j++) {
 +              if (s->kexlists[i][j].name == NULL) break;
 +              ssh2_pkt_addstring_commasep(s->pktout, s->kexlists[i][j].name);
 +          }
 +      }
        /* List client->server languages. Empty list. */
        ssh2_pkt_addstring_start(s->pktout);
        /* List server->client languages. Empty list. */
       * to.
       */
      {
 -      char *str, *preferred;
 +      char *str;
        int i, j, len;
  
        if (pktin->type != SSH2_MSG_KEXINIT) {
        s->warn_kex = s->warn_cscipher = s->warn_sccipher = FALSE;
  
        pktin->savedpos += 16;          /* skip garbage cookie */
 -      ssh_pkt_getstring(pktin, &str, &len);    /* key exchange algorithms */
 -        if (!str) {
 -            bombout(("KEXINIT packet was incomplete"));
 -            crStopV;
 -        }
  
 -      preferred = NULL;
 -      for (i = 0; i < s->n_preferred_kex; i++) {
 -          const struct ssh_kexes *k = s->preferred_kex[i];
 -          if (!k) {
 -              s->warn_kex = TRUE;
 -          } else {
 -              for (j = 0; j < k->nkexes; j++) {
 -                  if (!preferred) preferred = k->list[j]->name;
 -                  if (in_commasep_string(k->list[j]->name, str, len)) {
 -                      ssh->kex = k->list[j];
 -                      break;
 -                  }
 -              }
 -          }
 -          if (ssh->kex)
 -              break;
 -      }
 -      if (!ssh->kex) {
 -            bombout(("Couldn't agree a key exchange algorithm"
 -                     " (available: %.*s)", len, str));
 -          crStopV;
 -      }
 -      /*
 -       * Note that the server's guess is considered wrong if it doesn't match
 -       * the first algorithm in our list, even if it's still the algorithm
 -       * we end up using.
 -       */
 -      s->guessok = first_in_commasep_string(preferred, str, len);
 -      ssh_pkt_getstring(pktin, &str, &len);    /* host key algorithms */
 -        if (!str) {
 -            bombout(("KEXINIT packet was incomplete"));
 -            crStopV;
 -        }
 -      for (i = 0; i < lenof(hostkey_algs); i++) {
 -          if (in_commasep_string(hostkey_algs[i]->name, str, len)) {
 -              ssh->hostkey = hostkey_algs[i];
 -              break;
 +      s->guessok = FALSE;
 +      for (i = 0; i < NKEXLIST; i++) {
 +          ssh_pkt_getstring(pktin, &str, &len);
 +          if (!str) {
 +              bombout(("KEXINIT packet was incomplete"));
 +              crStopV;
            }
 -      }
 -      if (!ssh->hostkey) {
 -            bombout(("Couldn't agree a host key algorithm"
 -                     " (available: %.*s)", len, str));
 -          crStopV;
 -      }
  
 -      s->guessok = s->guessok &&
 -          first_in_commasep_string(hostkey_algs[0]->name, str, len);
 -      ssh_pkt_getstring(pktin, &str, &len);    /* client->server cipher */
 -        if (!str) {
 -            bombout(("KEXINIT packet was incomplete"));
 -            crStopV;
 -        }
 -      for (i = 0; i < s->n_preferred_ciphers; i++) {
 -          const struct ssh2_ciphers *c = s->preferred_ciphers[i];
 -          if (!c) {
 -              s->warn_cscipher = TRUE;
 -          } else {
 -              for (j = 0; j < c->nciphers; j++) {
 -                  if (in_commasep_string(c->list[j]->name, str, len)) {
 -                      s->cscipher_tobe = c->list[j];
 -                      break;
 -                  }
 -              }
 -          }
 -          if (s->cscipher_tobe)
 -              break;
 -      }
 -      if (!s->cscipher_tobe) {
 -            bombout(("Couldn't agree a client-to-server cipher"
 -                     " (available: %.*s)", len, str));
 -          crStopV;
 -      }
 +            /* If we've already selected a cipher which requires a
 +             * particular MAC, then just select that, and don't even
 +             * bother looking through the server's KEXINIT string for
 +             * MACs. */
 +            if (i == KEXLIST_CSMAC && s->cscipher_tobe &&
 +                s->cscipher_tobe->required_mac) {
 +                s->csmac_tobe = s->cscipher_tobe->required_mac;
 +                s->csmac_etm_tobe = !!(s->csmac_tobe->etm_name);
 +                goto matched;
 +            }
 +            if (i == KEXLIST_SCMAC && s->sccipher_tobe &&
 +                s->sccipher_tobe->required_mac) {
 +                s->scmac_tobe = s->sccipher_tobe->required_mac;
 +                s->scmac_etm_tobe = !!(s->scmac_tobe->etm_name);
 +                goto matched;
 +            }
  
 -      ssh_pkt_getstring(pktin, &str, &len);    /* server->client cipher */
 -        if (!str) {
 -            bombout(("KEXINIT packet was incomplete"));
 -            crStopV;
 -        }
 -      for (i = 0; i < s->n_preferred_ciphers; i++) {
 -          const struct ssh2_ciphers *c = s->preferred_ciphers[i];
 -          if (!c) {
 -              s->warn_sccipher = TRUE;
 -          } else {
 -              for (j = 0; j < c->nciphers; j++) {
 -                  if (in_commasep_string(c->list[j]->name, str, len)) {
 -                      s->sccipher_tobe = c->list[j];
 -                      break;
 +          for (j = 0; j < MAXKEXLIST; j++) {
 +              struct kexinit_algorithm *alg = &s->kexlists[i][j];
 +              if (alg->name == NULL) break;
 +              if (in_commasep_string(alg->name, str, len)) {
 +                  /* We've found a matching algorithm. */
 +                  if (i == KEXLIST_KEX || i == KEXLIST_HOSTKEY) {
 +                      /* Check if we might need to ignore first kex pkt */
 +                      if (j != 0 ||
 +                          !first_in_commasep_string(alg->name, str, len))
 +                          s->guessok = FALSE;
 +                  }
 +                  if (i == KEXLIST_KEX) {
 +                      ssh->kex = alg->u.kex.kex;
 +                      s->warn_kex = alg->u.kex.warn;
 +                  } else if (i == KEXLIST_HOSTKEY) {
 +                      ssh->hostkey = alg->u.hostkey;
 +                  } else if (i == KEXLIST_CSCIPHER) {
 +                      s->cscipher_tobe = alg->u.cipher.cipher;
 +                      s->warn_cscipher = alg->u.cipher.warn;
 +                  } else if (i == KEXLIST_SCCIPHER) {
 +                      s->sccipher_tobe = alg->u.cipher.cipher;
 +                      s->warn_sccipher = alg->u.cipher.warn;
 +                  } else if (i == KEXLIST_CSMAC) {
 +                      s->csmac_tobe = alg->u.mac.mac;
 +                      s->csmac_etm_tobe = alg->u.mac.etm;
 +                  } else if (i == KEXLIST_SCMAC) {
 +                      s->scmac_tobe = alg->u.mac.mac;
 +                      s->scmac_etm_tobe = alg->u.mac.etm;
 +                  } else if (i == KEXLIST_CSCOMP) {
 +                      s->cscomp_tobe = alg->u.comp;
 +                  } else if (i == KEXLIST_SCCOMP) {
 +                      s->sccomp_tobe = alg->u.comp;
                    }
 +                  goto matched;
                }
 +              if ((i == KEXLIST_CSCOMP || i == KEXLIST_SCCOMP) &&
 +                  in_commasep_string(alg->u.comp->delayed_name, str, len))
 +                  s->pending_compression = TRUE;  /* try this later */
            }
 -          if (s->sccipher_tobe)
 -              break;
 -      }
 -      if (!s->sccipher_tobe) {
 -            bombout(("Couldn't agree a server-to-client cipher"
 -                     " (available: %.*s)", len, str));
 +          bombout(("Couldn't agree a %s ((available: %.*s)",
 +                   kexlist_descr[i], len, str));
            crStopV;
 +        matched:;
        }
  
 -      ssh_pkt_getstring(pktin, &str, &len);    /* client->server mac */
 -        if (!str) {
 -            bombout(("KEXINIT packet was incomplete"));
 -            crStopV;
 -        }
 -      for (i = 0; i < s->nmacs; i++) {
 -          if (in_commasep_string(s->maclist[i]->name, str, len)) {
 -              s->csmac_tobe = s->maclist[i];
 -              break;
 -          }
 -      }
 -      ssh_pkt_getstring(pktin, &str, &len);    /* server->client mac */
 -        if (!str) {
 -            bombout(("KEXINIT packet was incomplete"));
 -            crStopV;
 -        }
 -      for (i = 0; i < s->nmacs; i++) {
 -          if (in_commasep_string(s->maclist[i]->name, str, len)) {
 -              s->scmac_tobe = s->maclist[i];
 -              break;
 -          }
 -      }
 -      ssh_pkt_getstring(pktin, &str, &len);  /* client->server compression */
 -        if (!str) {
 -            bombout(("KEXINIT packet was incomplete"));
 -            crStopV;
 -        }
 -      for (i = 0; i < lenof(compressions) + 1; i++) {
 -          const struct ssh_compress *c =
 -              i == 0 ? s->preferred_comp : compressions[i - 1];
 -          if (in_commasep_string(c->name, str, len)) {
 -              s->cscomp_tobe = c;
 -              break;
 -          } else if (in_commasep_string(c->delayed_name, str, len)) {
 -              if (s->userauth_succeeded) {
 -                  s->cscomp_tobe = c;
 -                  break;
 -              } else {
 -                  s->pending_compression = TRUE;  /* try this later */
 -              }
 -          }
 -      }
 -      ssh_pkt_getstring(pktin, &str, &len);  /* server->client compression */
 -        if (!str) {
 -            bombout(("KEXINIT packet was incomplete"));
 -            crStopV;
 -        }
 -      for (i = 0; i < lenof(compressions) + 1; i++) {
 -          const struct ssh_compress *c =
 -              i == 0 ? s->preferred_comp : compressions[i - 1];
 -          if (in_commasep_string(c->name, str, len)) {
 -              s->sccomp_tobe = c;
 -              break;
 -          } else if (in_commasep_string(c->delayed_name, str, len)) {
 -              if (s->userauth_succeeded) {
 -                  s->sccomp_tobe = c;
 -                  break;
 -              } else {
 -                  s->pending_compression = TRUE;  /* try this later */
 -              }
 -          }
 -      }
        if (s->pending_compression) {
            logevent("Server supports delayed compression; "
                     "will try this later");
          {
              int csbits, scbits;
  
 -            csbits = s->cscipher_tobe->keylen;
 -            scbits = s->sccipher_tobe->keylen;
 +            csbits = s->cscipher_tobe ? s->cscipher_tobe->real_keybits : 0;
 +            scbits = s->sccipher_tobe ? s->sccipher_tobe->real_keybits : 0;
              s->nbits = (csbits > scbits ? csbits : scbits);
          }
          /* The keys only have hlen-bit entropy, since they're based on
           * If we're doing Diffie-Hellman group exchange, start by
           * requesting a group.
           */
 -        if (!ssh->kex->pdata) {
 +        if (dh_is_gex(ssh->kex)) {
              logevent("Doing Diffie-Hellman group exchange");
              ssh->pkt_kctx = SSH2_PKTCTX_DHGEX;
              /*
              bombout(("unable to parse key exchange reply packet"));
              crStopV;
          }
 -        s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
 +        s->hkey = ssh->hostkey->newkey(ssh->hostkey,
 +                                       s->hostkeydata, s->hostkeylen);
          s->f = ssh2_pkt_getmp(pktin);
          if (!s->f) {
              bombout(("unable to parse key exchange reply packet"));
          set_busy_status(ssh->frontend, BUSY_NOT);
  
          hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen);
 -        if (!ssh->kex->pdata) {
 +        if (dh_is_gex(ssh->kex)) {
              if (!(ssh->remote_bugs & BUG_SSH2_OLDGEX))
                  hash_uint32(ssh->kex->hash, ssh->exhash, DH_MIN_SIZE);
              hash_uint32(ssh->kex->hash, ssh->exhash, s->pbits);
  
          dh_cleanup(ssh->kex_ctx);
          freebn(s->f);
 -        if (!ssh->kex->pdata) {
 +        if (dh_is_gex(ssh->kex)) {
              freebn(s->g);
              freebn(s->p);
          }
 +    } else if (ssh->kex->main_type == KEXTYPE_ECDH) {
 +
 +        logeventf(ssh, "Doing ECDH key exchange with curve %s and hash %s",
 +                  ssh_ecdhkex_curve_textname(ssh->kex),
 +                  ssh->kex->hash->text_name);
 +        ssh->pkt_kctx = SSH2_PKTCTX_ECDHKEX;
 +
 +        s->eckey = ssh_ecdhkex_newkey(ssh->kex);
 +        if (!s->eckey) {
 +            bombout(("Unable to generate key for ECDH"));
 +            crStopV;
 +        }
 +
 +        {
 +            char *publicPoint;
 +            int publicPointLength;
 +            publicPoint = ssh_ecdhkex_getpublic(s->eckey, &publicPointLength);
 +            if (!publicPoint) {
 +                ssh_ecdhkex_freekey(s->eckey);
 +                bombout(("Unable to encode public key for ECDH"));
 +                crStopV;
 +            }
 +            s->pktout = ssh2_pkt_init(SSH2_MSG_KEX_ECDH_INIT);
 +            ssh2_pkt_addstring_start(s->pktout);
 +            ssh2_pkt_addstring_data(s->pktout, publicPoint, publicPointLength);
 +            sfree(publicPoint);
 +        }
 +
 +        ssh2_pkt_send_noqueue(ssh, s->pktout);
 +
 +        crWaitUntilV(pktin);
 +        if (pktin->type != SSH2_MSG_KEX_ECDH_REPLY) {
 +            ssh_ecdhkex_freekey(s->eckey);
 +            bombout(("expected ECDH reply packet from server"));
 +            crStopV;
 +        }
 +
 +        ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
 +        if (!s->hostkeydata) {
 +            bombout(("unable to parse ECDH reply packet"));
 +            crStopV;
 +        }
 +        hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen);
 +        s->hkey = ssh->hostkey->newkey(ssh->hostkey,
 +                                       s->hostkeydata, s->hostkeylen);
 +
 +        {
 +            char *publicPoint;
 +            int publicPointLength;
 +            publicPoint = ssh_ecdhkex_getpublic(s->eckey, &publicPointLength);
 +            if (!publicPoint) {
 +                ssh_ecdhkex_freekey(s->eckey);
 +                bombout(("Unable to encode public key for ECDH hash"));
 +                crStopV;
 +            }
 +            hash_string(ssh->kex->hash, ssh->exhash,
 +                        publicPoint, publicPointLength);
 +            sfree(publicPoint);
 +        }
 +
 +        {
 +            char *keydata;
 +            int keylen;
 +            ssh_pkt_getstring(pktin, &keydata, &keylen);
 +            if (!keydata) {
 +                bombout(("unable to parse ECDH reply packet"));
 +                crStopV;
 +            }
 +            hash_string(ssh->kex->hash, ssh->exhash, keydata, keylen);
 +            s->K = ssh_ecdhkex_getkey(s->eckey, keydata, keylen);
 +            if (!s->K) {
 +                ssh_ecdhkex_freekey(s->eckey);
 +                bombout(("point received in ECDH was not valid"));
 +                crStopV;
 +            }
 +        }
 +
 +        ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
 +        if (!s->sigdata) {
 +            bombout(("unable to parse key exchange reply packet"));
 +            crStopV;
 +        }
 +
 +        ssh_ecdhkex_freekey(s->eckey);
      } else {
        logeventf(ssh, "Doing RSA key exchange with hash %s",
                  ssh->kex->hash->text_name);
          }
          hash_string(ssh->kex->hash, ssh->exhash,
                    s->hostkeydata, s->hostkeylen);
 -      s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
 +      s->hkey = ssh->hostkey->newkey(ssh->hostkey,
 +                                       s->hostkeydata, s->hostkeylen);
  
          {
              char *keydata;
      dmemdump(s->exchange_hash, ssh->kex->hash->hlen);
  #endif
  
 -    if (!s->hkey ||
 -      !ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen,
 +    if (!s->hkey) {
 +      bombout(("Server's host key is invalid"));
 +      crStopV;
 +    }
 +
 +    if (!ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen,
                                 (char *)s->exchange_hash,
                                 ssh->kex->hash->hlen)) {
 +#ifndef FUZZING
        bombout(("Server's host key did not match the signature supplied"));
        crStopV;
 +#endif
      }
  
      s->keystr = ssh->hostkey->fmtkey(s->hkey);
           * Authenticate remote host: verify host key. (We've already
           * checked the signature of the exchange hash.)
           */
 -        s->fingerprint = ssh->hostkey->fingerprint(s->hkey);
 +        s->fingerprint = ssh2_fingerprint(ssh->hostkey, s->hkey);
          logevent("Host key fingerprint is:");
          logevent(s->fingerprint);
          /* First check against manually configured host keys. */
                                              ssh->hostkey->keytype, s->keystr,
                                              s->fingerprint,
                                              ssh_dialog_callback, ssh);
 +#ifdef FUZZING
 +          s->dlgret = 1;
 +#endif
              if (s->dlgret < 0) {
                  do {
                      crReturnV;
           * the one we saw before.
           */
          if (strcmp(ssh->hostkey_str, s->keystr)) {
 +#ifndef FUZZING
              bombout(("Host key was different in repeat key exchange"));
              crStopV;
 +#endif
          }
          sfree(s->keystr);
      }
      if (ssh->cs_cipher_ctx)
        ssh->cscipher->free_context(ssh->cs_cipher_ctx);
      ssh->cscipher = s->cscipher_tobe;
 -    ssh->cs_cipher_ctx = ssh->cscipher->make_context();
 +    if (ssh->cscipher) ssh->cs_cipher_ctx = ssh->cscipher->make_context();
  
      if (ssh->cs_mac_ctx)
        ssh->csmac->free_context(ssh->cs_mac_ctx);
      ssh->csmac = s->csmac_tobe;
 -    ssh->cs_mac_ctx = ssh->csmac->make_context();
 +    ssh->csmac_etm = s->csmac_etm_tobe;
 +    if (ssh->csmac)
 +        ssh->cs_mac_ctx = ssh->csmac->make_context(ssh->cs_cipher_ctx);
  
      if (ssh->cs_comp_ctx)
        ssh->cscomp->compress_cleanup(ssh->cs_comp_ctx);
       * Set IVs on client-to-server keys. Here we use the exchange
       * hash from the _first_ key exchange.
       */
 -    {
 -      unsigned char keyspace[SSH2_KEX_MAX_HASH_LEN * SSH2_MKKEY_ITERS];
 -      assert(sizeof(keyspace) >= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
 -      ssh2_mkkey(ssh,s->K,s->exchange_hash,'C',keyspace);
 -      assert((ssh->cscipher->keylen+7) / 8 <=
 -             ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
 -      ssh->cscipher->setkey(ssh->cs_cipher_ctx, keyspace);
 -      ssh2_mkkey(ssh,s->K,s->exchange_hash,'A',keyspace);
 -      assert(ssh->cscipher->blksize <=
 -             ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
 -      ssh->cscipher->setiv(ssh->cs_cipher_ctx, keyspace);
 -      ssh2_mkkey(ssh,s->K,s->exchange_hash,'E',keyspace);
 -      assert(ssh->csmac->len <=
 -             ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
 -      ssh->csmac->setkey(ssh->cs_mac_ctx, keyspace);
 -      smemclr(keyspace, sizeof(keyspace));
 -    }
 -
 -    logeventf(ssh, "Initialised %.200s client->server encryption",
 -            ssh->cscipher->text_name);
 -    logeventf(ssh, "Initialised %.200s client->server MAC algorithm",
 -            ssh->csmac->text_name);
 +    if (ssh->cscipher) {
 +      unsigned char *key;
 +
 +      key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'C',
 +                         ssh->cscipher->padded_keybytes);
 +      ssh->cscipher->setkey(ssh->cs_cipher_ctx, key);
 +        smemclr(key, ssh->cscipher->padded_keybytes);
 +        sfree(key);
 +
 +      key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'A',
 +                         ssh->cscipher->blksize);
 +      ssh->cscipher->setiv(ssh->cs_cipher_ctx, key);
 +        smemclr(key, ssh->cscipher->blksize);
 +        sfree(key);
 +    }
 +    if (ssh->csmac) {
 +      unsigned char *key;
 +
 +      key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'E',
 +                         ssh->csmac->keylen);
 +      ssh->csmac->setkey(ssh->cs_mac_ctx, key);
 +        smemclr(key, ssh->csmac->keylen);
 +        sfree(key);
 +    }
 +
 +    if (ssh->cscipher)
 +      logeventf(ssh, "Initialised %.200s client->server encryption",
 +                ssh->cscipher->text_name);
 +    if (ssh->csmac)
 +      logeventf(ssh, "Initialised %.200s client->server MAC algorithm%s%s",
 +                ssh->csmac->text_name,
 +                ssh->csmac_etm ? " (in ETM mode)" : "",
 +                ssh->cscipher->required_mac ? " (required by cipher)" : "");
      if (ssh->cscomp->text_name)
        logeventf(ssh, "Initialised %s compression",
                  ssh->cscomp->text_name);
       */
      if (ssh->sc_cipher_ctx)
        ssh->sccipher->free_context(ssh->sc_cipher_ctx);
 -    ssh->sccipher = s->sccipher_tobe;
 -    ssh->sc_cipher_ctx = ssh->sccipher->make_context();
 +    if (s->sccipher_tobe) {
 +      ssh->sccipher = s->sccipher_tobe;
 +      ssh->sc_cipher_ctx = ssh->sccipher->make_context();
 +    }
  
      if (ssh->sc_mac_ctx)
        ssh->scmac->free_context(ssh->sc_mac_ctx);
 -    ssh->scmac = s->scmac_tobe;
 -    ssh->sc_mac_ctx = ssh->scmac->make_context();
 +    if (s->scmac_tobe) {
 +      ssh->scmac = s->scmac_tobe;
 +      ssh->scmac_etm = s->scmac_etm_tobe;
 +      ssh->sc_mac_ctx = ssh->scmac->make_context(ssh->sc_cipher_ctx);
 +    }
  
      if (ssh->sc_comp_ctx)
        ssh->sccomp->decompress_cleanup(ssh->sc_comp_ctx);
       * Set IVs on server-to-client keys. Here we use the exchange
       * hash from the _first_ key exchange.
       */
 -    {
 -      unsigned char keyspace[SSH2_KEX_MAX_HASH_LEN * SSH2_MKKEY_ITERS];
 -      assert(sizeof(keyspace) >= ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
 -      ssh2_mkkey(ssh,s->K,s->exchange_hash,'D',keyspace);
 -      assert((ssh->sccipher->keylen+7) / 8 <=
 -             ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
 -      ssh->sccipher->setkey(ssh->sc_cipher_ctx, keyspace);
 -      ssh2_mkkey(ssh,s->K,s->exchange_hash,'B',keyspace);
 -      assert(ssh->sccipher->blksize <=
 -             ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
 -      ssh->sccipher->setiv(ssh->sc_cipher_ctx, keyspace);
 -      ssh2_mkkey(ssh,s->K,s->exchange_hash,'F',keyspace);
 -      assert(ssh->scmac->len <=
 -             ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
 -      ssh->scmac->setkey(ssh->sc_mac_ctx, keyspace);
 -      smemclr(keyspace, sizeof(keyspace));
 -    }
 -    logeventf(ssh, "Initialised %.200s server->client encryption",
 -            ssh->sccipher->text_name);
 -    logeventf(ssh, "Initialised %.200s server->client MAC algorithm",
 -            ssh->scmac->text_name);
 +    if (ssh->sccipher) {
 +      unsigned char *key;
 +
 +      key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'D',
 +                         ssh->sccipher->padded_keybytes);
 +      ssh->sccipher->setkey(ssh->sc_cipher_ctx, key);
 +        smemclr(key, ssh->sccipher->padded_keybytes);
 +        sfree(key);
 +
 +      key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'B',
 +                         ssh->sccipher->blksize);
 +      ssh->sccipher->setiv(ssh->sc_cipher_ctx, key);
 +        smemclr(key, ssh->sccipher->blksize);
 +        sfree(key);
 +    }
 +    if (ssh->scmac) {
 +      unsigned char *key;
 +
 +      key = ssh2_mkkey(ssh, s->K, s->exchange_hash, 'F',
 +                         ssh->scmac->keylen);
 +      ssh->scmac->setkey(ssh->sc_mac_ctx, key);
 +        smemclr(key, ssh->scmac->keylen);
 +        sfree(key);
 +    }
 +    if (ssh->sccipher)
 +      logeventf(ssh, "Initialised %.200s server->client encryption",
 +                ssh->sccipher->text_name);
 +    if (ssh->scmac)
 +      logeventf(ssh, "Initialised %.200s server->client MAC algorithm%s%s",
 +                ssh->scmac->text_name,
 +                ssh->scmac_etm ? " (in ETM mode)" : "",
 +                ssh->sccipher->required_mac ? " (required by cipher)" : "");
      if (ssh->sccomp->text_name)
        logeventf(ssh, "Initialised %s decompression",
                  ssh->sccomp->text_name);
  /*
   * Add data to an SSH-2 channel output buffer.
   */
 -static void ssh2_add_channel_data(struct ssh_channel *c, char *buf,
 +static void ssh2_add_channel_data(struct ssh_channel *c, const char *buf,
                                  int len)
  {
      bufchain_add(&c->v.v2.outbuffer, buf, len);
@@@ -7579,8 -7278,7 +7580,8 @@@ static void ssh2_channel_init(struct ss
  /*
   * Construct the common parts of a CHANNEL_OPEN.
   */
 -static struct Packet *ssh2_chanopen_init(struct ssh_channel *c, char *type)
 +static struct Packet *ssh2_chanopen_init(struct ssh_channel *c,
 +                                         const char *type)
  {
      struct Packet *pktout;
  
@@@ -7627,8 -7325,7 +7628,8 @@@ static void ssh2_queue_chanreq_handler(
   * the server initiated channel closure before we saw the response)
   * and the handler should free any storage it's holding.
   */
 -static struct Packet *ssh2_chanreq_init(struct ssh_channel *c, char *type,
 +static struct Packet *ssh2_chanreq_init(struct ssh_channel *c,
 +                                        const char *type,
                                        cchandler_fn_t handler, void *ctx)
  {
      struct Packet *pktout;
@@@ -8264,7 -7961,8 +8265,8 @@@ static void ssh2_msg_channel_open_failu
              reason_code = 0; /* ensure reasons[reason_code] in range */
          ssh_pkt_getstring(pktin, &reason_string, &reason_length);
          logeventf(ssh, "Forwarded connection refused by server: %s [%.*s]",
-                   reasons[reason_code], reason_length, reason_string);
+                   reasons[reason_code], reason_length,
+                   NULLTOEMPTY(reason_string));
  
          pfd_close(c->u.pfd.pf);
      } else if (c->type == CHAN_ZOMBIE) {
@@@ -8343,7 -8041,7 +8345,7 @@@ static void ssh2_msg_channel_request(Ss
                   !memcmp(type, "exit-signal", 11)) {
  
            int is_plausible = TRUE, is_int = FALSE;
 -          char *fmt_sig = "", *fmt_msg = "";
 +            char *fmt_sig = NULL, *fmt_msg = NULL;
            char *msg;
            int msglen = 0, core = FALSE;
            /* ICK: older versions of OpenSSH (e.g. 3.4p1)
                /* ignore lang tag */
            } /* else don't attempt to parse */
            logeventf(ssh, "Server exited on signal%s%s%s",
 -                    fmt_sig, core ? " (core dumped)" : "",
 -                    fmt_msg);
 -          if (*fmt_sig) sfree(fmt_sig);
 -          if (*fmt_msg) sfree(fmt_msg);
 +                    fmt_sig ? fmt_sig : "",
 +                      core ? " (core dumped)" : "",
 +                    fmt_msg ? fmt_msg : "");
 +          sfree(fmt_sig);
 +            sfree(fmt_msg);
            reply = SSH2_MSG_CHANNEL_SUCCESS;
  
        }
@@@ -8542,7 -8239,7 +8544,7 @@@ static void ssh2_msg_channel_open(Ssh s
      char *peeraddr;
      int peeraddrlen;
      int peerport;
 -    char *error = NULL;
 +    const char *error = NULL;
      struct ssh_channel *c;
      unsigned remid, winsize, pktsize;
      unsigned our_winsize_override = 0;
        char *addrstr;
  
        ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen);
-       addrstr = snewn(peeraddrlen+1, char);
-       memcpy(addrstr, peeraddr, peeraddrlen);
-       addrstr[peeraddrlen] = '\0';
+       addrstr = dupprintf("%.*s", peeraddrlen, NULLTOEMPTY(peeraddr));
        peerport = ssh_pkt_getuint32(pktin);
  
        logeventf(ssh, "Received X11 connect request from %s:%d",
        char *shost;
        int shostlen;
        ssh_pkt_getstring(pktin, &shost, &shostlen);/* skip address */
-         pf.shost = dupprintf("%.*s", shostlen, shost);
+         pf.shost = dupprintf("%.*s", shostlen, NULLTOEMPTY(shost));
        pf.sport = ssh_pkt_getuint32(pktin);
        ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen);
        peerport = ssh_pkt_getuint32(pktin);
        realpf = find234(ssh->rportfwds, &pf, NULL);
        logeventf(ssh, "Received remote port %s:%d open request "
-                 "from %s:%d", pf.shost, pf.sport, peeraddr, peerport);
+                 "from %.*s:%d", pf.shost, pf.sport,
+                   peeraddrlen, NULLTOEMPTY(peeraddr), peerport);
          sfree(pf.shost);
  
        if (realpf == NULL) {
@@@ -8955,7 -8651,7 +8956,7 @@@ static void ssh2_response_authconn(stru
          do_ssh2_authconn(c->ssh, NULL, 0, pktin);
  }
  
 -static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 +static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
                             struct Packet *pktin)
  {
      struct do_ssh2_authconn_state {
        int got_username;
        void *publickey_blob;
        int publickey_bloblen;
 -      int publickey_encrypted;
 +      int privatekey_available, privatekey_encrypted;
        char *publickey_algorithm;
        char *publickey_comment;
        unsigned char agent_request[5], *agent_response, *agentp;
        s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
        if (!filename_is_null(s->keyfile)) {
            int keytype;
 -          logeventf(ssh, "Reading private key file \"%.150s\"",
 +          logeventf(ssh, "Reading key file \"%.150s\"",
                      filename_to_str(s->keyfile));
            keytype = key_type(s->keyfile);
 -          if (keytype == SSH_KEYTYPE_SSH2) {
 +          if (keytype == SSH_KEYTYPE_SSH2 ||
 +                keytype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 ||
 +                keytype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) {
                const char *error;
                s->publickey_blob =
                    ssh2_userkey_loadpub(s->keyfile,
                                         &s->publickey_bloblen, 
                                         &s->publickey_comment, &error);
                if (s->publickey_blob) {
 -                  s->publickey_encrypted =
 +                  s->privatekey_available = (keytype == SSH_KEYTYPE_SSH2);
 +                    if (!s->privatekey_available)
 +                        logeventf(ssh, "Key file contains public key only");
 +                  s->privatekey_encrypted =
                        ssh2_userkey_encrypted(s->keyfile, NULL);
                } else {
                    char *msgbuf;
 -                  logeventf(ssh, "Unable to load private key (%s)", 
 +                  logeventf(ssh, "Unable to load key (%s)", 
                              error);
 -                  msgbuf = dupprintf("Unable to load private key file "
 +                  msgbuf = dupprintf("Unable to load key file "
                                       "\"%.150s\" (%s)\r\n",
                                       filename_to_str(s->keyfile),
                                       error);
                }
  
            } else if (s->can_pubkey && s->publickey_blob &&
 -                     !s->tried_pubkey_config) {
 +                     s->privatekey_available && !s->tried_pubkey_config) {
  
                struct ssh2_userkey *key;   /* not live over crReturn */
                char *passphrase;           /* not live over crReturn */
                key = NULL;
                while (!key) {
                    const char *error;  /* not live over crReturn */
 -                  if (s->publickey_encrypted) {
 +                  if (s->privatekey_encrypted) {
                        /*
                         * Get a passphrase from the user.
                         */
                    int prompt_len; /* not live over crReturn */
                    
                    {
 -                      char *msg;
 +                      const char *msg;
                        if (changereq_first_time)
                            msg = "Server requested password change";
                        else
                    s->cur_prompt->to_server = TRUE;
                    s->cur_prompt->name = dupstr("New SSH password");
                    s->cur_prompt->instruction =
-                       dupprintf("%.*s", prompt_len, prompt);
+                       dupprintf("%.*s", prompt_len, NULLTOEMPTY(prompt));
                    s->cur_prompt->instr_reqd = TRUE;
                    /*
                     * There's no explicit requirement in the protocol
  
      /* Clear up various bits and pieces from authentication. */
      if (s->publickey_blob) {
 +      sfree(s->publickey_algorithm);
        sfree(s->publickey_blob);
        sfree(s->publickey_comment);
      }
       * Transfer data!
       */
      if (ssh->ldisc)
 -      ldisc_send(ssh->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */
 +      ldisc_echoedit_update(ssh->ldisc);  /* cause ldisc to notice changes */
      if (ssh->mainchan)
        ssh->send_ok = 1;
      while (1) {
@@@ -10705,13 -10395,13 +10706,13 @@@ static void ssh2_msg_disconnect(Ssh ssh
      logevent(buf);
      sfree(buf);
      buf = dupprintf("Disconnection message text: %.*s",
-                   msglen, msg);
+                   msglen, NULLTOEMPTY(msg));
      logevent(buf);
      bombout(("Server sent disconnect message\ntype %d (%s):\n\"%.*s\"",
             reason,
             (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ?
             ssh2_disconnect_reasons[reason] : "unknown",
-            msglen, msg));
+            msglen, NULLTOEMPTY(msg)));
      sfree(buf);
  }
  
@@@ -10725,7 -10415,7 +10726,7 @@@ static void ssh2_msg_debug(Ssh ssh, str
      ssh2_pkt_getbool(pktin);
      ssh_pkt_getstring(pktin, &msg, &msglen);
  
-     logeventf(ssh, "Remote debug message: %.*s", msglen, msg);
+     logeventf(ssh, "Remote debug message: %.*s", msglen, NULLTOEMPTY(msg));
  }
  
  static void ssh2_msg_transport(Ssh ssh, struct Packet *pktin)
@@@ -10876,10 -10566,10 +10877,10 @@@ static void ssh2_timer(void *ctx, unsig
      }
  }
  
 -static void ssh2_protocol(Ssh ssh, void *vin, int inlen,
 +static void ssh2_protocol(Ssh ssh, const void *vin, int inlen,
                          struct Packet *pktin)
  {
 -    unsigned char *in = (unsigned char *)vin;
 +    const unsigned char *in = (const unsigned char *)vin;
      if (ssh->state == SSH_STATE_CLOSED)
        return;
  
        do_ssh2_authconn(ssh, in, inlen, pktin);
  }
  
 -static void ssh2_bare_connection_protocol(Ssh ssh, void *vin, int inlen,
 +static void ssh2_bare_connection_protocol(Ssh ssh, const void *vin, int inlen,
                                            struct Packet *pktin)
  {
 -    unsigned char *in = (unsigned char *)vin;
 +    const unsigned char *in = (const unsigned char *)vin;
      if (ssh->state == SSH_STATE_CLOSED)
        return;
  
@@@ -10923,8 -10613,7 +10924,8 @@@ static void ssh_cache_conf_values(Ssh s
   * Returns an error message, or NULL on success.
   */
  static const char *ssh_init(void *frontend_handle, void **backend_handle,
 -                          Conf *conf, char *host, int port, char **realhost,
 +                          Conf *conf,
 +                            const char *host, int port, char **realhost,
                            int nodelay, int keepalive)
  {
      const char *p;
      ssh->X11_fwd_enabled = FALSE;
      ssh->connshare = NULL;
      ssh->attempting_connshare = FALSE;
 +    ssh->session_started = FALSE;
  
      *backend_handle = ssh;
  
@@@ -11177,8 -10865,7 +11178,8 @@@ static void ssh_free(void *handle
  static void ssh_reconfig(void *handle, Conf *conf)
  {
      Ssh ssh = (Ssh) handle;
 -    char *rekeying = NULL, rekey_mandatory = FALSE;
 +    const char *rekeying = NULL;
 +    int rekey_mandatory = FALSE;
      unsigned long old_max_data_size;
      int i, rekey_time;
  
  /*
   * Called to send data down the SSH connection.
   */
 -static int ssh_send(void *handle, char *buf, int len)
 +static int ssh_send(void *handle, const char *buf, int len)
  {
      Ssh ssh = (Ssh) handle;
  
      if (ssh == NULL || ssh->s == NULL || ssh->protocol == NULL)
        return 0;
  
 -    ssh->protocol(ssh, (unsigned char *)buf, len, 0);
 +    ssh->protocol(ssh, (const unsigned char *)buf, len, 0);
  
      return ssh_sendbuffer(ssh);
  }
@@@ -11458,7 -11145,7 +11459,7 @@@ static void ssh_special(void *handle, T
        }
      } else {
        /* Is is a POSIX signal? */
 -      char *signame = NULL;
 +      const char *signame = NULL;
        if (code == TS_SIGABRT) signame = "ABRT";
        if (code == TS_SIGALRM) signame = "ALRM";
        if (code == TS_SIGFPE)  signame = "FPE";
@@@ -11575,8 -11262,7 +11576,8 @@@ static void ssh_unthrottle(void *handle
      ssh_process_queued_incoming_data(ssh);
  }
  
 -void ssh_send_port_open(void *channel, char *hostname, int port, char *org)
 +void ssh_send_port_open(void *channel, const char *hostname, int port,
 +                        const char *org)
  {
      struct ssh_channel *c = (struct ssh_channel *)channel;
      Ssh ssh = c->ssh;
@@@ -11701,7 -11387,6 +11702,7 @@@ Backend ssh_backend = 
      ssh_provide_logctx,
      ssh_unthrottle,
      ssh_cfg_info,
 +    ssh_test_for_upstream,
      "ssh",
      PROT_SSH,
      22
diff --combined windows/winnpc.c
index 85a3c3ffc3b6e37b252ba0c7f1d92cd41d33fa7d,5927df32f01418f5325472913952c28b7011fa1f..e5d1d7a667c5cd44b0a6fb11c103155c072f582f
@@@ -16,8 -16,8 +16,8 @@@
  
  #include "winsecur.h"
  
 -Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug,
 -                          int overlapped);
 +Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H,
 +                          Plug plug, int overlapped);
  
  Socket new_named_pipe_client(const char *pipename, Plug plug)
  {
@@@ -79,7 -79,6 +79,6 @@@
          ret = new_error_socket(err, plug);
          sfree(err);
          CloseHandle(pipehandle);
-         sfree(usersid);
          return ret;
      }
  
          sfree(err);
          CloseHandle(pipehandle);
          LocalFree(psd);
-         sfree(usersid);
          return ret;
      }
  
      LocalFree(psd);
-     sfree(usersid);
  
 -    return make_handle_socket(pipehandle, pipehandle, plug, TRUE);
 +    return make_handle_socket(pipehandle, pipehandle, NULL, plug, TRUE);
  }
  
  #endif /* !defined NO_SECURITY */
diff --combined windows/winpgnt.c
index 2109d1c6feb94c56f243da497aa8a0512f1468da,f637153833cb676711b3befc32d17698bcbd8e8a..6e8016408186a9fe3a9e00672bb2aa8642749d40
@@@ -15,7 -15,6 +15,7 @@@
  #include "misc.h"
  #include "tree234.h"
  #include "winsecur.h"
 +#include "pageant.h"
  #include "licence.h"
  
  #include <shellapi.h>
  
  #define AGENT_COPYDATA_ID 0x804e50ba   /* random goop */
  
 -/*
 - * FIXME: maybe some day we can sort this out ...
 - */
 -#define AGENT_MAX_MSGLEN  8192
 -
  /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
   * wParam are used by Windows, and should be masked off, so we shouldn't
   * attempt to store information in them. Hence all these identifiers have
@@@ -71,7 -75,7 +71,7 @@@ static int initial_menuitems_count
  /*
   * Print a modal (Really Bad) message box and perform a fatal exit.
   */
 -void modalfatalbox(char *fmt, ...)
 +void modalfatalbox(const char *fmt, ...)
  {
      va_list ap;
      char *buf;
@@@ -111,17 -115,71 +111,17 @@@ static void unmungestr(char *in, char *
      return;
  }
  
 -static tree234 *rsakeys, *ssh2keys;
 -
  static int has_security;
  
 -/*
 - * Forward references
 - */
 -static void *make_keylist1(int *length);
 -static void *make_keylist2(int *length);
 -static void *get_keylist1(int *length);
 -static void *get_keylist2(int *length);
 -
 -/*
 - * We need this to link with the RSA code, because rsaencrypt()
 - * pads its data with random bytes. Since we only use rsadecrypt()
 - * and the signing functions, which are deterministic, this should
 - * never be called.
 - *
 - * If it _is_ called, there is a _serious_ problem, because it
 - * won't generate true random numbers. So we must scream, panic,
 - * and exit immediately if that should happen.
 - */
 -int random_byte(void)
 -{
 -    MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
 -    exit(0);
 -    /* this line can't be reached but it placates MSVC's warnings :-) */
 -    return 0;
 -}
 -
 -/*
 - * Blob structure for passing to the asymmetric SSH-2 key compare
 - * function, prototyped here.
 - */
 -struct blob {
 -    unsigned char *blob;
 -    int len;
 -};
 -static int cmpkeys_ssh2_asymm(void *av, void *bv);
 -
  struct PassphraseProcStruct {
      char **passphrase;
      char *comment;
  };
  
 -static tree234 *passphrases = NULL;
 -
 -/* 
 - * After processing a list of filenames, we want to forget the
 - * passphrases.
 - */
 -static void forget_passphrases(void)
 -{
 -    while (count234(passphrases) > 0) {
 -      char *pp = index234(passphrases, 0);
 -      smemclr(pp, strlen(pp));
 -      delpos234(passphrases, 0);
 -      free(pp);
 -    }
 -}
 -
  /*
   * Dialog-box function for the Licence box.
   */
 -static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
 +static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg,
                                WPARAM wParam, LPARAM lParam)
  {
      switch (msg) {
  /*
   * Dialog-box function for the About box.
   */
 -static int CALLBACK AboutProc(HWND hwnd, UINT msg,
 +static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
                              WPARAM wParam, LPARAM lParam)
  {
      switch (msg) {
@@@ -188,7 -246,7 +188,7 @@@ static HWND passphrase_box
  /*
   * Dialog-box function for the passphrase box.
   */
 -static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
 +static INT_PTR CALLBACK PassphraseProc(HWND hwnd, UINT msg,
                                   WPARAM wParam, LPARAM lParam)
  {
      static char **passphrase = NULL;
@@@ -272,7 -330,7 +272,7 @@@ void old_keyfile_warning(void
  /*
   * Update the visible key list.
   */
 -static void keylist_update(void)
 +void keylist_update(void)
  {
      struct RSAKey *rkey;
      struct ssh2_userkey *skey;
  
      if (keylist) {
        SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
 -      for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
 +      for (i = 0; NULL != (rkey = pageant_nth_ssh1_key(i)); i++) {
            char listentry[512], *p;
            /*
             * Replace two spaces in the fingerprint with tabs, for
            SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
                               0, (LPARAM) listentry);
        }
 -      for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
 +      for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) {
            char *listentry, *p;
 -          int fp_len;
 +          int pos;
            /*
 -           * Replace two spaces in the fingerprint with tabs, for
 -           * nice alignment in the box.
 +           * Replace spaces with tabs in the fingerprint prefix, for
 +           * nice alignment in the list box, until we encounter a :
 +           * meaning we're into the fingerprint proper.
             */
 -          p = skey->alg->fingerprint(skey->data);
 +          p = ssh2_fingerprint(skey->alg, skey->data);
              listentry = dupprintf("%s\t%s", p, skey->comment);
 -            fp_len = strlen(listentry);
              sfree(p);
  
 -          p = strchr(listentry, ' ');
 -          if (p && p < listentry + fp_len)
 -              *p = '\t';
 -          p = strchr(listentry, ' ');
 -          if (p && p < listentry + fp_len)
 -              *p = '\t';
 +            pos = 0;
 +            while (1) {
 +                pos += strcspn(listentry + pos, " :");
 +                if (listentry[pos] == ':' || !listentry[pos])
 +                    break;
 +                listentry[pos++] = '\t';
 +            }
  
            SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
                               (LPARAM) listentry);
      }
  }
  
 -/*
 - * This function loads a key from a file and adds it.
 - */
 -static void add_keyfile(Filename *filename)
 +static void answer_msg(void *msgv)
  {
 -    char *passphrase;
 -    struct RSAKey *rkey = NULL;
 -    struct ssh2_userkey *skey = NULL;
 -    int needs_pass;
 -    int ret;
 -    int attempts;
 -    char *comment;
 -    const char *error = NULL;
 -    int type;
 -    int original_pass;
 -      
 -    type = key_type(filename);
 -    if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
 -      char *msg = dupprintf("Couldn't load this key (%s)",
 -                            key_type_to_str(type));
 -      message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
 -                  HELPCTXID(errors_cantloadkey));
 -      sfree(msg);
 -      return;
 -    }
 -
 -    /*
 -     * See if the key is already loaded (in the primary Pageant,
 -     * which may or may not be us).
 -     */
 -    {
 -      void *blob;
 -      unsigned char *keylist, *p;
 -      int i, nkeys, bloblen, keylistlen;
 -
 -      if (type == SSH_KEYTYPE_SSH1) {
 -          if (!rsakey_pubblob(filename, &blob, &bloblen, NULL, &error)) {
 -              char *msg = dupprintf("Couldn't load private key (%s)", error);
 -              message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
 -                          HELPCTXID(errors_cantloadkey));
 -              sfree(msg);
 -              return;
 -          }
 -          keylist = get_keylist1(&keylistlen);
 -      } else {
 -          unsigned char *blob2;
 -          blob = ssh2_userkey_loadpub(filename, NULL, &bloblen,
 -                                      NULL, &error);
 -          if (!blob) {
 -              char *msg = dupprintf("Couldn't load private key (%s)", error);
 -              message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
 -                          HELPCTXID(errors_cantloadkey));
 -              sfree(msg);
 -              return;
 -          }
 -          /* For our purposes we want the blob prefixed with its length */
 -          blob2 = snewn(bloblen+4, unsigned char);
 -          PUT_32BIT(blob2, bloblen);
 -          memcpy(blob2 + 4, blob, bloblen);
 -          sfree(blob);
 -          blob = blob2;
 -
 -          keylist = get_keylist2(&keylistlen);
 -      }
 -      if (keylist) {
 -          if (keylistlen < 4) {
 -              MessageBox(NULL, "Received broken key list?!", APPNAME,
 -                         MB_OK | MB_ICONERROR);
 -              return;
 -          }
 -          nkeys = toint(GET_32BIT(keylist));
 -          if (nkeys < 0) {
 -              MessageBox(NULL, "Received broken key list?!", APPNAME,
 -                         MB_OK | MB_ICONERROR);
 -              return;
 -          }
 -          p = keylist + 4;
 -          keylistlen -= 4;
 -
 -          for (i = 0; i < nkeys; i++) {
 -              if (!memcmp(blob, p, bloblen)) {
 -                  /* Key is already present; we can now leave. */
 -                  sfree(keylist);
 -                  sfree(blob);
 -                  return;
 -              }
 -              /* Now skip over public blob */
 -              if (type == SSH_KEYTYPE_SSH1) {
 -                  int n = rsa_public_blob_len(p, keylistlen);
 -                  if (n < 0) {
 -                      MessageBox(NULL, "Received broken key list?!", APPNAME,
 -                                 MB_OK | MB_ICONERROR);
 -                      return;
 -                  }
 -                  p += n;
 -                  keylistlen -= n;
 -              } else {
 -                  int n;
 -                  if (keylistlen < 4) {
 -                      MessageBox(NULL, "Received broken key list?!", APPNAME,
 -                                 MB_OK | MB_ICONERROR);
 -                      return;
 -                  }
 -                  n = toint(4 + GET_32BIT(p));
 -                  if (n < 0 || keylistlen < n) {
 -                      MessageBox(NULL, "Received broken key list?!", APPNAME,
 -                                 MB_OK | MB_ICONERROR);
 -                      return;
 -                  }
 -                  p += n;
 -                  keylistlen -= n;
 -              }
 -              /* Now skip over comment field */
 -              {
 -                  int n;
 -                  if (keylistlen < 4) {
 -                      MessageBox(NULL, "Received broken key list?!", APPNAME,
 -                                 MB_OK | MB_ICONERROR);
 -                      return;
 -                  }
 -                  n = toint(4 + GET_32BIT(p));
 -                  if (n < 0 || keylistlen < n) {
 -                      MessageBox(NULL, "Received broken key list?!", APPNAME,
 -                                 MB_OK | MB_ICONERROR);
 -                      return;
 -                  }
 -                  p += n;
 -                  keylistlen -= n;
 -              }
 -          }
 -
 -          sfree(keylist);
 -      }
 -
 -      sfree(blob);
 -    }
 -
 -    error = NULL;
 -    if (type == SSH_KEYTYPE_SSH1)
 -      needs_pass = rsakey_encrypted(filename, &comment);
 -    else
 -      needs_pass = ssh2_userkey_encrypted(filename, &comment);
 -    attempts = 0;
 -    if (type == SSH_KEYTYPE_SSH1)
 -      rkey = snew(struct RSAKey);
 -    passphrase = NULL;
 -    original_pass = 0;
 -    do {
 -        burnstr(passphrase);
 -        passphrase = NULL;
 -
 -      if (needs_pass) {
 -          /* try all the remembered passphrases first */
 -          char *pp = index234(passphrases, attempts);
 -          if(pp) {
 -              passphrase = dupstr(pp);
 -          } else {
 -              int dlgret;
 -                struct PassphraseProcStruct pps;
 -
 -                pps.passphrase = &passphrase;
 -                pps.comment = comment;
 -
 -              original_pass = 1;
 -              dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
 -                                      NULL, PassphraseProc, (LPARAM) &pps);
 -              passphrase_box = NULL;
 -              if (!dlgret) {
 -                  if (comment)
 -                      sfree(comment);
 -                  if (type == SSH_KEYTYPE_SSH1)
 -                      sfree(rkey);
 -                  return;                    /* operation cancelled */
 -              }
 -
 -                assert(passphrase != NULL);
 -          }
 -      } else
 -          passphrase = dupstr("");
 -
 -      if (type == SSH_KEYTYPE_SSH1)
 -          ret = loadrsakey(filename, rkey, passphrase, &error);
 -      else {
 -          skey = ssh2_load_userkey(filename, passphrase, &error);
 -          if (skey == SSH2_WRONG_PASSPHRASE)
 -              ret = -1;
 -          else if (!skey)
 -              ret = 0;
 -          else
 -              ret = 1;
 -      }
 -      attempts++;
 -    } while (ret == -1);
 -
 -    if(original_pass && ret) {
 -        /* If they typed in an ok passphrase, remember it */
 -      addpos234(passphrases, passphrase, 0);
 +    unsigned char *msg = (unsigned char *)msgv;
 +    unsigned msglen;
 +    void *reply;
 +    int replylen;
 +
 +    msglen = GET_32BIT(msg);
 +    if (msglen > AGENT_MAX_MSGLEN) {
 +        reply = pageant_failure_msg(&replylen);
      } else {
 -        /* Otherwise, destroy it */
 -        burnstr(passphrase);
 -    }
 -    passphrase = NULL;
 -
 -    if (comment)
 -      sfree(comment);
 -    if (ret == 0) {
 -      char *msg = dupprintf("Couldn't load private key (%s)", error);
 -      message_box(msg, APPNAME, MB_OK | MB_ICONERROR,
 -                  HELPCTXID(errors_cantloadkey));
 -      sfree(msg);
 -      if (type == SSH_KEYTYPE_SSH1)
 -          sfree(rkey);
 -      return;
 -    }
 -    if (type == SSH_KEYTYPE_SSH1) {
 -      if (already_running) {
 -          unsigned char *request, *response;
 -          void *vresponse;
 -          int reqlen, clen, resplen, ret;
 -
 -          clen = strlen(rkey->comment);
 -
 -          reqlen = 4 + 1 +           /* length, message type */
 -              4 +                    /* bit count */
 -              ssh1_bignum_length(rkey->modulus) +
 -              ssh1_bignum_length(rkey->exponent) +
 -              ssh1_bignum_length(rkey->private_exponent) +
 -              ssh1_bignum_length(rkey->iqmp) +
 -              ssh1_bignum_length(rkey->p) +
 -              ssh1_bignum_length(rkey->q) + 4 + clen  /* comment */
 -              ;
 -
 -          request = snewn(reqlen, unsigned char);
 -
 -          request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
 -          reqlen = 5;
 -          PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
 -          reqlen += 4;
 -          reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
 -          reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
 -          reqlen +=
 -              ssh1_write_bignum(request + reqlen,
 -                                rkey->private_exponent);
 -          reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
 -          reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
 -          reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
 -          PUT_32BIT(request + reqlen, clen);
 -          memcpy(request + reqlen + 4, rkey->comment, clen);
 -          reqlen += 4 + clen;
 -          PUT_32BIT(request, reqlen - 4);
 -
 -          ret = agent_query(request, reqlen, &vresponse, &resplen,
 -                            NULL, NULL);
 -          assert(ret == 1);
 -          response = vresponse;
 -          if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
 -              MessageBox(NULL, "The already running Pageant "
 -                         "refused to add the key.", APPNAME,
 -                         MB_OK | MB_ICONERROR);
 -
 -          sfree(request);
 -          sfree(response);
 -      } else {
 -          if (add234(rsakeys, rkey) != rkey)
 -              sfree(rkey);           /* already present, don't waste RAM */
 -      }
 -    } else {
 -      if (already_running) {
 -          unsigned char *request, *response;
 -          void *vresponse;
 -          int reqlen, alglen, clen, keybloblen, resplen, ret;
 -          alglen = strlen(skey->alg->name);
 -          clen = strlen(skey->comment);
 -
 -          keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
 -
 -          reqlen = 4 + 1 +           /* length, message type */
 -              4 + alglen +           /* algorithm name */
 -              keybloblen +           /* key data */
 -              4 + clen               /* comment */
 -              ;
 -
 -          request = snewn(reqlen, unsigned char);
 -
 -          request[4] = SSH2_AGENTC_ADD_IDENTITY;
 -          reqlen = 5;
 -          PUT_32BIT(request + reqlen, alglen);
 -          reqlen += 4;
 -          memcpy(request + reqlen, skey->alg->name, alglen);
 -          reqlen += alglen;
 -          reqlen += skey->alg->openssh_fmtkey(skey->data,
 -                                              request + reqlen,
 -                                              keybloblen);
 -          PUT_32BIT(request + reqlen, clen);
 -          memcpy(request + reqlen + 4, skey->comment, clen);
 -          reqlen += clen + 4;
 -          PUT_32BIT(request, reqlen - 4);
 -
 -          ret = agent_query(request, reqlen, &vresponse, &resplen,
 -                            NULL, NULL);
 -          assert(ret == 1);
 -          response = vresponse;
 -          if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
 -              MessageBox(NULL, "The already running Pageant "
 -                         "refused to add the key.", APPNAME,
 -                         MB_OK | MB_ICONERROR);
 -
 -          sfree(request);
 -          sfree(response);
 -      } else {
 -          if (add234(ssh2keys, skey) != skey) {
 -              skey->alg->freekey(skey->data);
 -              sfree(skey);           /* already present, don't waste RAM */
 -          }
 -      }
 +        reply = pageant_handle_msg(msg + 4, msglen, &replylen, NULL, NULL);
 +        if (replylen > AGENT_MAX_MSGLEN) {
 +            smemclr(reply, replylen);
 +            sfree(reply);
 +            reply = pageant_failure_msg(&replylen);
 +        }
      }
 -}
 -
 -/*
 - * Create an SSH-1 key list in a malloc'ed buffer; return its
 - * length.
 - */
 -static void *make_keylist1(int *length)
 -{
 -    int i, nkeys, len;
 -    struct RSAKey *key;
 -    unsigned char *blob, *p, *ret;
 -    int bloblen;
  
      /*
 -     * Count up the number and length of keys we hold.
 +     * Windows Pageant answers messages in place, by overwriting the
 +     * input message buffer.
       */
 -    len = 4;
 -    nkeys = 0;
 -    for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
 -      nkeys++;
 -      blob = rsa_public_blob(key, &bloblen);
 -      len += bloblen;
 -      sfree(blob);
 -      len += 4 + strlen(key->comment);
 -    }
 -
 -    /* Allocate the buffer. */
 -    p = ret = snewn(len, unsigned char);
 -    if (length) *length = len;
 -
 -    PUT_32BIT(p, nkeys);
 -    p += 4;
 -    for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
 -      blob = rsa_public_blob(key, &bloblen);
 -      memcpy(p, blob, bloblen);
 -      p += bloblen;
 -      sfree(blob);
 -      PUT_32BIT(p, strlen(key->comment));
 -      memcpy(p + 4, key->comment, strlen(key->comment));
 -      p += 4 + strlen(key->comment);
 -    }
 -
 -    assert(p - ret == len);
 -    return ret;
 +    memcpy(msg, reply, replylen);
 +    smemclr(reply, replylen);
 +    sfree(reply);
  }
  
 -/*
 - * Create an SSH-2 key list in a malloc'ed buffer; return its
 - * length.
 - */
 -static void *make_keylist2(int *length)
 +static void win_add_keyfile(Filename *filename)
  {
 -    struct ssh2_userkey *key;
 -    int i, len, nkeys;
 -    unsigned char *blob, *p, *ret;
 -    int bloblen;
 +    char *err;
 +    int ret;
 +    char *passphrase = NULL;
  
      /*
 -     * Count up the number and length of keys we hold.
 +     * Try loading the key without a passphrase. (Or rather, without a
 +     * _new_ passphrase; pageant_add_keyfile will take care of trying
 +     * all the passphrases we've already stored.)
       */
 -    len = 4;
 -    nkeys = 0;
 -    for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
 -      nkeys++;
 -      len += 4;              /* length field */
 -      blob = key->alg->public_blob(key->data, &bloblen);
 -      len += bloblen;
 -      sfree(blob);
 -      len += 4 + strlen(key->comment);
 +    ret = pageant_add_keyfile(filename, NULL, &err);
 +    if (ret == PAGEANT_ACTION_OK) {
 +        goto done;
 +    } else if (ret == PAGEANT_ACTION_FAILURE) {
 +        goto error;
      }
  
 -    /* Allocate the buffer. */
 -    p = ret = snewn(len, unsigned char);
 -    if (length) *length = len;
 -
      /*
 -     * Packet header is the obvious five bytes, plus four
 -     * bytes for the key count.
 +     * OK, a passphrase is needed, and we've been given the key
 +     * comment to use in the passphrase prompt.
       */
 -    PUT_32BIT(p, nkeys);
 -    p += 4;
 -    for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
 -      blob = key->alg->public_blob(key->data, &bloblen);
 -      PUT_32BIT(p, bloblen);
 -      p += 4;
 -      memcpy(p, blob, bloblen);
 -      p += bloblen;
 -      sfree(blob);
 -      PUT_32BIT(p, strlen(key->comment));
 -      memcpy(p + 4, key->comment, strlen(key->comment));
 -      p += 4 + strlen(key->comment);
 -    }
 -
 -    assert(p - ret == len);
 -    return ret;
 -}
 -
 -/*
 - * Acquire a keylist1 from the primary Pageant; this means either
 - * calling make_keylist1 (if that's us) or sending a message to the
 - * primary Pageant (if it's not).
 - */
 -static void *get_keylist1(int *length)
 -{
 -    void *ret;
 +    while (1) {
 +        INT_PTR dlgret;
 +        struct PassphraseProcStruct pps;
  
 -    if (already_running) {
 -      unsigned char request[5], *response;
 -      void *vresponse;
 -      int resplen, retval;
 -      request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
 -      PUT_32BIT(request, 4);
 -
 -      retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
 -      assert(retval == 1);
 -      response = vresponse;
 -      if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
 -            sfree(response);
 -          return NULL;
 -        }
 +        pps.passphrase = &passphrase;
 +        pps.comment = err;
 +        dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
 +                                NULL, PassphraseProc, (LPARAM) &pps);
 +        passphrase_box = NULL;
  
 -      ret = snewn(resplen-5, unsigned char);
 -      memcpy(ret, response+5, resplen-5);
 -      sfree(response);
 +        if (!dlgret)
 +            goto done;                       /* operation cancelled */
  
 -      if (length)
 -          *length = resplen-5;
 -    } else {
 -      ret = make_keylist1(length);
 -    }
 -    return ret;
 -}
 +        sfree(err);
  
 -/*
 - * Acquire a keylist2 from the primary Pageant; this means either
 - * calling make_keylist2 (if that's us) or sending a message to the
 - * primary Pageant (if it's not).
 - */
 -static void *get_keylist2(int *length)
 -{
 -    void *ret;
 +        assert(passphrase != NULL);
  
 -    if (already_running) {
 -      unsigned char request[5], *response;
 -      void *vresponse;
 -      int resplen, retval;
 -
 -      request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
 -      PUT_32BIT(request, 4);
 -
 -      retval = agent_query(request, 5, &vresponse, &resplen, NULL, NULL);
 -      assert(retval == 1);
 -      response = vresponse;
 -      if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) {
 -            sfree(response);
 -          return NULL;
 +        ret = pageant_add_keyfile(filename, passphrase, &err);
 +        if (ret == PAGEANT_ACTION_OK) {
 +            goto done;
 +        } else if (ret == PAGEANT_ACTION_FAILURE) {
 +            goto error;
          }
  
 -      ret = snewn(resplen-5, unsigned char);
 -      memcpy(ret, response+5, resplen-5);
 -      sfree(response);
 -
 -      if (length)
 -          *length = resplen-5;
 -    } else {
 -      ret = make_keylist2(length);
 -    }
 -    return ret;
 -}
 -
 -/*
 - * This is the main agent function that answers messages.
 - */
 -static void answer_msg(void *msg)
 -{
 -    unsigned char *p = msg;
 -    unsigned char *ret = msg;
 -    unsigned char *msgend;
 -    int type;
 -
 -    /*
 -     * Get the message length.
 -     */
 -    msgend = p + 4 + GET_32BIT(p);
 -
 -    /*
 -     * Get the message type.
 -     */
 -    if (msgend < p+5)
 -      goto failure;
 -    type = p[4];
 -
 -    p += 5;
 -    switch (type) {
 -      case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
 -      /*
 -       * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
 -       */
 -      {
 -          int len;
 -          void *keylist;
 -
 -          ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
 -          keylist = make_keylist1(&len);
 -          if (len + 5 > AGENT_MAX_MSGLEN) {
 -              sfree(keylist);
 -              goto failure;
 -          }
 -          PUT_32BIT(ret, len + 1);
 -          memcpy(ret + 5, keylist, len);
 -          sfree(keylist);
 -      }
 -      break;
 -      case SSH2_AGENTC_REQUEST_IDENTITIES:
 -      /*
 -       * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
 -       */
 -      {
 -          int len;
 -          void *keylist;
 -
 -          ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
 -          keylist = make_keylist2(&len);
 -          if (len + 5 > AGENT_MAX_MSGLEN) {
 -              sfree(keylist);
 -              goto failure;
 -          }
 -          PUT_32BIT(ret, len + 1);
 -          memcpy(ret + 5, keylist, len);
 -          sfree(keylist);
 -      }
 -      break;
 -      case SSH1_AGENTC_RSA_CHALLENGE:
 -      /*
 -       * Reply with either SSH1_AGENT_RSA_RESPONSE or
 -       * SSH_AGENT_FAILURE, depending on whether we have that key
 -       * or not.
 -       */
 -      {
 -          struct RSAKey reqkey, *key;
 -          Bignum challenge, response;
 -          unsigned char response_source[48], response_md5[16];
 -          struct MD5Context md5c;
 -          int i, len;
 -
 -          p += 4;
 -          i = ssh1_read_bignum(p, msgend - p, &reqkey.exponent);
 -          if (i < 0)
 -              goto failure;
 -          p += i;
 -          i = ssh1_read_bignum(p, msgend - p, &reqkey.modulus);
 -          if (i < 0) {
 -                freebn(reqkey.exponent);
 -              goto failure;
 -            }
 -          p += i;
 -          i = ssh1_read_bignum(p, msgend - p, &challenge);
 -          if (i < 0) {
 -                freebn(reqkey.exponent);
 -                freebn(reqkey.modulus);
 -              goto failure;
 -            }
 -          p += i;
 -          if (msgend < p+16) {
 -              freebn(reqkey.exponent);
 -              freebn(reqkey.modulus);
 -              freebn(challenge);
 -              goto failure;
 -          }
 -          memcpy(response_source + 32, p, 16);
 -          p += 16;
 -          if (msgend < p+4 ||
 -              GET_32BIT(p) != 1 ||
 -              (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
 -              freebn(reqkey.exponent);
 -              freebn(reqkey.modulus);
 -              freebn(challenge);
 -              goto failure;
 -          }
 -          response = rsadecrypt(challenge, key);
 -          for (i = 0; i < 32; i++)
 -              response_source[i] = bignum_byte(response, 31 - i);
 -
 -          MD5Init(&md5c);
 -          MD5Update(&md5c, response_source, 48);
 -          MD5Final(response_md5, &md5c);
 -          smemclr(response_source, 48);       /* burn the evidence */
 -          freebn(response);          /* and that evidence */
 -          freebn(challenge);         /* yes, and that evidence */
 -          freebn(reqkey.exponent);   /* and free some memory ... */
 -          freebn(reqkey.modulus);    /* ... while we're at it. */
 -
 -          /*
 -           * Packet is the obvious five byte header, plus sixteen
 -           * bytes of MD5.
 -           */
 -          len = 5 + 16;
 -          PUT_32BIT(ret, len - 4);
 -          ret[4] = SSH1_AGENT_RSA_RESPONSE;
 -          memcpy(ret + 5, response_md5, 16);
 -      }
 -      break;
 -      case SSH2_AGENTC_SIGN_REQUEST:
 -      /*
 -       * Reply with either SSH2_AGENT_SIGN_RESPONSE or
 -       * SSH_AGENT_FAILURE, depending on whether we have that key
 -       * or not.
 -       */
 -      {
 -          struct ssh2_userkey *key;
 -          struct blob b;
 -          unsigned char *data, *signature;
 -          int datalen, siglen, len;
 -
 -          if (msgend < p+4)
 -              goto failure;
 -          b.len = toint(GET_32BIT(p));
 -            if (b.len < 0 || b.len > msgend - (p+4))
 -                goto failure;
 -          p += 4;
 -          b.blob = p;
 -          p += b.len;
 -          if (msgend < p+4)
 -              goto failure;
 -          datalen = toint(GET_32BIT(p));
 -          p += 4;
 -          if (datalen < 0 || datalen > msgend - p)
 -              goto failure;
 -          data = p;
 -          key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
 -          if (!key)
 -              goto failure;
 -          signature = key->alg->sign(key->data, data, datalen, &siglen);
 -          len = 5 + 4 + siglen;
 -          PUT_32BIT(ret, len - 4);
 -          ret[4] = SSH2_AGENT_SIGN_RESPONSE;
 -          PUT_32BIT(ret + 5, siglen);
 -          memcpy(ret + 5 + 4, signature, siglen);
 -          sfree(signature);
 -      }
 -      break;
 -      case SSH1_AGENTC_ADD_RSA_IDENTITY:
 -      /*
 -       * Add to the list and return SSH_AGENT_SUCCESS, or
 -       * SSH_AGENT_FAILURE if the key was malformed.
 -       */
 -      {
 -          struct RSAKey *key;
 -          char *comment;
 -            int n, commentlen;
 -
 -          key = snew(struct RSAKey);
 -          memset(key, 0, sizeof(struct RSAKey));
 -
 -          n = makekey(p, msgend - p, key, NULL, 1);
 -          if (n < 0) {
 -              freersakey(key);
 -              sfree(key);
 -              goto failure;
 -          }
 -          p += n;
 -
 -          n = makeprivate(p, msgend - p, key);
 -          if (n < 0) {
 -              freersakey(key);
 -              sfree(key);
 -              goto failure;
 -          }
 -          p += n;
 -
 -          n = ssh1_read_bignum(p, msgend - p, &key->iqmp);  /* p^-1 mod q */
 -          if (n < 0) {
 -              freersakey(key);
 -              sfree(key);
 -              goto failure;
 -          }
 -          p += n;
 -
 -          n = ssh1_read_bignum(p, msgend - p, &key->p);  /* p */
 -          if (n < 0) {
 -              freersakey(key);
 -              sfree(key);
 -              goto failure;
 -          }
 -          p += n;
 -
 -          n = ssh1_read_bignum(p, msgend - p, &key->q);  /* q */
 -          if (n < 0) {
 -              freersakey(key);
 -              sfree(key);
 -              goto failure;
 -          }
 -          p += n;
 -
 -          if (msgend < p+4) {
 -              freersakey(key);
 -              sfree(key);
 -              goto failure;
 -          }
 -            commentlen = toint(GET_32BIT(p));
 -
 -          if (commentlen < 0 || commentlen > msgend - p) {
 -              freersakey(key);
 -              sfree(key);
 -              goto failure;
 -          }
 -
 -          comment = snewn(commentlen+1, char);
 -          if (comment) {
 -              memcpy(comment, p + 4, commentlen);
 -                comment[commentlen] = '\0';
 -              key->comment = comment;
 -          }
 -          PUT_32BIT(ret, 1);
 -          ret[4] = SSH_AGENT_FAILURE;
 -          if (add234(rsakeys, key) == key) {
 -              keylist_update();
 -              ret[4] = SSH_AGENT_SUCCESS;
 -          } else {
 -              freersakey(key);
 -              sfree(key);
 -          }
 -      }
 -      break;
 -      case SSH2_AGENTC_ADD_IDENTITY:
 -      /*
 -       * Add to the list and return SSH_AGENT_SUCCESS, or
 -       * SSH_AGENT_FAILURE if the key was malformed.
 -       */
 -      {
 -          struct ssh2_userkey *key;
 -          char *comment, *alg;
 -          int alglen, commlen;
 -          int bloblen;
 -
 -
 -          if (msgend < p+4)
 -              goto failure;
 -          alglen = toint(GET_32BIT(p));
 -          p += 4;
 -          if (alglen < 0 || alglen > msgend - p)
 -              goto failure;
 -          alg = p;
 -          p += alglen;
 -
 -          key = snew(struct ssh2_userkey);
 -          /* Add further algorithm names here. */
 -          if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
 -              key->alg = &ssh_rsa;
 -          else if (alglen == 7 && !memcmp(alg, "ssh-dss", 7))
 -              key->alg = &ssh_dss;
 -          else {
 -              sfree(key);
 -              goto failure;
 -          }
 -
 -          bloblen = msgend - p;
 -          key->data = key->alg->openssh_createkey(&p, &bloblen);
 -          if (!key->data) {
 -              sfree(key);
 -              goto failure;
 -          }
 -
 -          /*
 -           * p has been advanced by openssh_createkey, but
 -           * certainly not _beyond_ the end of the buffer.
 -           */
 -          assert(p <= msgend);
 -
 -          if (msgend < p+4) {
 -              key->alg->freekey(key->data);
 -              sfree(key);
 -              goto failure;
 -          }
 -          commlen = toint(GET_32BIT(p));
 -          p += 4;
 -
 -          if (commlen < 0 || commlen > msgend - p) {
 -              key->alg->freekey(key->data);
 -              sfree(key);
 -              goto failure;
 -          }
 -          comment = snewn(commlen + 1, char);
 -          if (comment) {
 -              memcpy(comment, p, commlen);
 -              comment[commlen] = '\0';
 -          }
 -          key->comment = comment;
 -
 -          PUT_32BIT(ret, 1);
 -          ret[4] = SSH_AGENT_FAILURE;
 -          if (add234(ssh2keys, key) == key) {
 -              keylist_update();
 -              ret[4] = SSH_AGENT_SUCCESS;
 -          } else {
 -              key->alg->freekey(key->data);
 -              sfree(key->comment);
 -              sfree(key);
 -          }
 -      }
 -      break;
 -      case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
 -      /*
 -       * Remove from the list and return SSH_AGENT_SUCCESS, or
 -       * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
 -       * start with.
 -       */
 -      {
 -          struct RSAKey reqkey, *key;
 -          int n;
 -
 -          n = makekey(p, msgend - p, &reqkey, NULL, 0);
 -          if (n < 0)
 -              goto failure;
 -
 -          key = find234(rsakeys, &reqkey, NULL);
 -          freebn(reqkey.exponent);
 -          freebn(reqkey.modulus);
 -          PUT_32BIT(ret, 1);
 -          ret[4] = SSH_AGENT_FAILURE;
 -          if (key) {
 -              del234(rsakeys, key);
 -              keylist_update();
 -              freersakey(key);
 -              sfree(key);
 -              ret[4] = SSH_AGENT_SUCCESS;
 -          }
 -      }
 -      break;
 -      case SSH2_AGENTC_REMOVE_IDENTITY:
 -      /*
 -       * Remove from the list and return SSH_AGENT_SUCCESS, or
 -       * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
 -       * start with.
 -       */
 -      {
 -          struct ssh2_userkey *key;
 -          struct blob b;
 -
 -          if (msgend < p+4)
 -              goto failure;
 -          b.len = toint(GET_32BIT(p));
 -          p += 4;
 -
 -          if (b.len < 0 || b.len > msgend - p)
 -              goto failure;
 -          b.blob = p;
 -          p += b.len;
 -
 -          key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
 -          if (!key)
 -              goto failure;
 -
 -          PUT_32BIT(ret, 1);
 -          ret[4] = SSH_AGENT_FAILURE;
 -          if (key) {
 -              del234(ssh2keys, key);
 -              keylist_update();
 -              key->alg->freekey(key->data);
 -              sfree(key);
 -              ret[4] = SSH_AGENT_SUCCESS;
 -          }
 -      }
 -      break;
 -      case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
 -      /*
 -       * Remove all SSH-1 keys. Always returns success.
 -       */
 -      {
 -          struct RSAKey *rkey;
 -
 -          while ((rkey = index234(rsakeys, 0)) != NULL) {
 -              del234(rsakeys, rkey);
 -              freersakey(rkey);
 -              sfree(rkey);
 -          }
 -          keylist_update();
 -
 -          PUT_32BIT(ret, 1);
 -          ret[4] = SSH_AGENT_SUCCESS;
 -      }
 -      break;
 -      case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
 -      /*
 -       * Remove all SSH-2 keys. Always returns success.
 -       */
 -      {
 -          struct ssh2_userkey *skey;
 -
 -          while ((skey = index234(ssh2keys, 0)) != NULL) {
 -              del234(ssh2keys, skey);
 -              skey->alg->freekey(skey->data);
 -              sfree(skey);
 -          }
 -          keylist_update();
 -
 -          PUT_32BIT(ret, 1);
 -          ret[4] = SSH_AGENT_SUCCESS;
 -      }
 -      break;
 -      default:
 -      failure:
 -      /*
 -       * Unrecognised message. Return SSH_AGENT_FAILURE.
 -       */
 -      PUT_32BIT(ret, 1);
 -      ret[4] = SSH_AGENT_FAILURE;
 -      break;
 -    }
 -}
 -
 -/*
 - * Key comparison function for the 2-3-4 tree of RSA keys.
 - */
 -static int cmpkeys_rsa(void *av, void *bv)
 -{
 -    struct RSAKey *a = (struct RSAKey *) av;
 -    struct RSAKey *b = (struct RSAKey *) bv;
 -    Bignum am, bm;
 -    int alen, blen;
 -
 -    am = a->modulus;
 -    bm = b->modulus;
 -    /*
 -     * Compare by length of moduli.
 -     */
 -    alen = bignum_bitcount(am);
 -    blen = bignum_bitcount(bm);
 -    if (alen > blen)
 -      return +1;
 -    else if (alen < blen)
 -      return -1;
 -    /*
 -     * Now compare by moduli themselves.
 -     */
 -    alen = (alen + 7) / 8;           /* byte count */
 -    while (alen-- > 0) {
 -      int abyte, bbyte;
 -      abyte = bignum_byte(am, alen);
 -      bbyte = bignum_byte(bm, alen);
 -      if (abyte > bbyte)
 -          return +1;
 -      else if (abyte < bbyte)
 -          return -1;
 -    }
 -    /*
 -     * Give up.
 -     */
 -    return 0;
 -}
 -
 -/*
 - * Key comparison function for the 2-3-4 tree of SSH-2 keys.
 - */
 -static int cmpkeys_ssh2(void *av, void *bv)
 -{
 -    struct ssh2_userkey *a = (struct ssh2_userkey *) av;
 -    struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
 -    int i;
 -    int alen, blen;
 -    unsigned char *ablob, *bblob;
 -    int c;
 -
 -    /*
 -     * Compare purely by public blob.
 -     */
 -    ablob = a->alg->public_blob(a->data, &alen);
 -    bblob = b->alg->public_blob(b->data, &blen);
 -
 -    c = 0;
 -    for (i = 0; i < alen && i < blen; i++) {
 -      if (ablob[i] < bblob[i]) {
 -          c = -1;
 -          break;
 -      } else if (ablob[i] > bblob[i]) {
 -          c = +1;
 -          break;
 -      }
 +        smemclr(passphrase, strlen(passphrase));
 +        sfree(passphrase);
 +        passphrase = NULL;
      }
 -    if (c == 0 && i < alen)
 -      c = +1;                        /* a is longer */
 -    if (c == 0 && i < blen)
 -      c = -1;                        /* a is longer */
 -
 -    sfree(ablob);
 -    sfree(bblob);
  
 -    return c;
 -}
 -
 -/*
 - * Key comparison function for looking up a blob in the 2-3-4 tree
 - * of SSH-2 keys.
 - */
 -static int cmpkeys_ssh2_asymm(void *av, void *bv)
 -{
 -    struct blob *a = (struct blob *) av;
 -    struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
 -    int i;
 -    int alen, blen;
 -    unsigned char *ablob, *bblob;
 -    int c;
 -
 -    /*
 -     * Compare purely by public blob.
 -     */
 -    ablob = a->blob;
 -    alen = a->len;
 -    bblob = b->alg->public_blob(b->data, &blen);
 -
 -    c = 0;
 -    for (i = 0; i < alen && i < blen; i++) {
 -      if (ablob[i] < bblob[i]) {
 -          c = -1;
 -          break;
 -      } else if (ablob[i] > bblob[i]) {
 -          c = +1;
 -          break;
 -      }
 +  error:
 +    message_box(err, APPNAME, MB_OK | MB_ICONERROR,
 +                HELPCTXID(errors_cantloadkey));
 +  done:
 +    if (passphrase) {
 +        smemclr(passphrase, strlen(passphrase));
 +        sfree(passphrase);
      }
 -    if (c == 0 && i < alen)
 -      c = +1;                        /* a is longer */
 -    if (c == 0 && i < blen)
 -      c = -1;                        /* a is longer */
 -
 -    sfree(bblob);
 -
 -    return c;
 +    sfree(err);
 +    return;
  }
  
  /*
@@@ -441,7 -1453,7 +441,7 @@@ static void prompt_add_keyfile(void
        if(strlen(filelist) > of.nFileOffset) {
            /* Only one filename returned? */
              Filename *fn = filename_from_str(filelist);
 -          add_keyfile(fn);
 +          win_add_keyfile(fn);
              filename_free(fn);
          } else {
            /* we are returned a bunch of strings, end to
            while (*filewalker != '\0') {
                char *filename = dupcat(dir, "\\", filewalker, NULL);
                  Filename *fn = filename_from_str(filename);
 -              add_keyfile(fn);
 +              win_add_keyfile(fn);
                  filename_free(fn);
                sfree(filename);
                filewalker += strlen(filewalker) + 1;
        }
  
        keylist_update();
 -      forget_passphrases();
 +      pageant_forget_passphrases();
      }
      sfree(filelist);
  }
  /*
   * Dialog-box function for the key list box.
   */
 -static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
 +static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg,
                                WPARAM wParam, LPARAM lParam)
  {
      struct RSAKey *rkey;
  
        keylist = hwnd;
        {
 -          static int tabs[] = { 35, 60, 210 };
 +          static int tabs[] = { 35, 75, 250 };
            SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
                               sizeof(tabs) / sizeof(*tabs),
                               (LPARAM) tabs);
                                numSelected, (WPARAM)selectedArray);
                
                itemNum = numSelected - 1;
 -              rCount = count234(rsakeys);
 -              sCount = count234(ssh2keys);
 +              rCount = pageant_count_ssh1_keys();
 +              sCount = pageant_count_ssh2_keys();
                
                /* go through the non-rsakeys until we've covered them all, 
                 * and/or we're out of selected items to check. note that
                 * we go *backwards*, to avoid complications from deleting
                 * things hence altering the offset of subsequent items
                 */
 -          for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
 -                      skey = index234(ssh2keys, i);
 +                for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
 +                    skey = pageant_nth_ssh2_key(i);
                        
 -                      if (selectedArray[itemNum] == rCount + i) {
 -                              del234(ssh2keys, skey);
 -                              skey->alg->freekey(skey->data);
 -                              sfree(skey);
 -                              itemNum--; 
 -                      }
 +                    if (selectedArray[itemNum] == rCount + i) {
 +                        pageant_delete_ssh2_key(skey);
 +                        skey->alg->freekey(skey->data);
 +                        sfree(skey);
 +                        itemNum--;
 +                    }
                }
                
                /* do the same for the rsa keys */
                for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
 -                      rkey = index234(rsakeys, i);
 -
 -                      if(selectedArray[itemNum] == i) {
 -                              del234(rsakeys, rkey);
 -                              freersakey(rkey);
 -                              sfree(rkey);
 -                              itemNum--;
 -                      }
 +                    rkey = pageant_nth_ssh1_key(i);
 +
 +                    if(selectedArray[itemNum] == i) {
 +                        pageant_delete_ssh1_key(rkey);
 +                        freersakey(rkey);
 +                        sfree(rkey);
 +                        itemNum--;
 +                    }
                }
  
                sfree(selectedArray); 
        case WM_HELP:
          {
              int id = ((LPHELPINFO)lParam)->iCtrlId;
 -            char *topic = NULL;
 +            const char *topic = NULL;
              switch (id) {
                case 100: topic = WINHELP_CTX_pageant_keylist; break;
                case 101: topic = WINHELP_CTX_pageant_addkey; break;
@@@ -753,6 -1765,7 +753,6 @@@ PSID get_default_sid(void
  static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                                WPARAM wParam, LPARAM lParam)
  {
 -    int ret;
      static int menuinprogress;
      static UINT msgTaskbarCreated = 0;
  
            menuinprogress = 1;
            update_sessions();
            SetForegroundWindow(hwnd);
 -          ret = TrackPopupMenu(systray_menu,
 -                               TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
 -                               TPM_RIGHTBUTTON,
 -                               wParam, lParam, 0, hwnd, NULL);
 +          TrackPopupMenu(systray_menu,
 +                         TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
 +                         TPM_RIGHTBUTTON,
 +                         wParam, lParam, 0, hwnd, NULL);
            menuinprogress = 0;
        }
        break;
        case WM_SYSCOMMAND:
        switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
          case IDM_PUTTY:
 -          if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
 +          if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
                                 SW_SHOW) <= 32) {
                MessageBox(NULL, "Unable to execute PuTTY!",
                           "Error", MB_OK | MB_ICONERROR);
                    GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
                    strcpy(param, "@");
                    strcat(param, mii.dwTypeData);
 -                  if((int)ShellExecute(hwnd, NULL, putty_path, param,
 +                  if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, param,
                                         _T(""), SW_SHOW) <= 32) {
                        MessageBox(NULL, "Unable to execute PuTTY!", "Error",
                                   MB_OK | MB_ICONERROR);
                        debug(("couldn't get default SID\n"));
  #endif
                          CloseHandle(filemap);
-                         sfree(ourself);
                        return 0;
                      }
  
                                 rc));
  #endif
                          CloseHandle(filemap);
-                         sfree(ourself);
                          sfree(ourself2);
                        return 0;
                    }
                          !EqualSid(mapowner, ourself2)) {
                          CloseHandle(filemap);
                          LocalFree(psd);
-                         sfree(ourself);
                          sfree(ourself2);
                        return 0;      /* security ID mismatch! */
                      }
                    debug(("security stuff matched\n"));
  #endif
                      LocalFree(psd);
-                     sfree(ourself);
                      sfree(ourself2);
                } else {
  #ifdef DEBUG_IPC
  /*
   * Fork and Exec the command in cmdline. [DBW]
   */
 -void spawn_cmd(char *cmdline, char * args, int show)
 +void spawn_cmd(const char *cmdline, const char *args, int show)
  {
      if (ShellExecute(NULL, _T("open"), cmdline,
                     args, NULL, show) <= (HINSTANCE) 32) {
@@@ -1030,7 -2039,7 +1026,7 @@@ int WINAPI WinMain(HINSTANCE inst, HINS
  {
      WNDCLASS wndclass;
      MSG msg;
 -    char *command = NULL;
 +    const char *command = NULL;
      int added_keys = 0;
      int argc, i;
      char **argv, **argstart;
      already_running = agent_exists();
  
      /*
 -     * Initialise storage for RSA keys.
 +     * Initialise the cross-platform Pageant code.
       */
      if (!already_running) {
 -      rsakeys = newtree234(cmpkeys_rsa);
 -      ssh2keys = newtree234(cmpkeys_ssh2);
 +        pageant_init();
      }
  
 -    /*
 -     * Initialise storage for short-term passphrase cache.
 -     */
 -    passphrases = newtree234(NULL);
 -
      /*
       * Process the command line and add keys as listed on it.
       */
            break;
        } else {
              Filename *fn = filename_from_str(argv[i]);
 -          add_keyfile(fn);
 +          win_add_keyfile(fn);
              filename_free(fn);
            added_keys = TRUE;
        }
       * Forget any passphrase that we retained while going over
       * command line keyfiles.
       */
 -    forget_passphrases();
 +    pageant_forget_passphrases();
  
      if (command) {
        char *args;
        session_menu = CreateMenu();
        AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
        AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
 -                 (UINT) session_menu, "&Saved Sessions");
 +                 (UINT_PTR) session_menu, "&Saved Sessions");
        AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
      }
      AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,