Browse Source

Merge branch 'thirdparty_dev' into dev

# Conflicts:
#	source/putty/ssh2transport.c

Source commit: 904f8304a88ba00d5188847a121423e01179ecd1
Martin Prikryl 6 years ago
parent
commit
879e3c77cf

+ 10 - 0
source/putty/agentf.c

@@ -159,6 +159,16 @@ static const struct ChannelVtable agentf_channelvt = {
     chan_no_exit_status,
     chan_no_exit_signal,
     chan_no_exit_signal_numeric,
+    chan_no_run_shell,
+    chan_no_run_command,
+    chan_no_run_subsystem,
+    chan_no_enable_x11_forwarding,
+    chan_no_enable_agent_forwarding,
+    chan_no_allocate_pty,
+    chan_no_set_env,
+    chan_no_send_break,
+    chan_no_send_signal,
+    chan_no_change_window_size,
     chan_no_request_response,
 };
 

+ 2 - 0
source/putty/callback.c

@@ -81,6 +81,8 @@ void delete_callbacks_for_context(CALLBACK_SET void *ctx)
 
     cbhead = newhead;
     cbtail = newtail;
+    if (newtail)
+        newtail->next = NULL;
 }
 
 void queue_toplevel_callback(CALLBACK_SET toplevel_callback_fn_t fn, void *ctx)

+ 3 - 0
source/putty/defs.h

@@ -61,6 +61,9 @@ typedef struct Frontend Frontend;
 
 typedef struct Ssh Ssh;
 
+typedef struct SftpServer SftpServer;
+typedef struct SftpServerVtable SftpServerVtable;
+
 typedef struct Channel Channel;
 typedef struct SshChannel SshChannel;
 typedef struct mainchan mainchan;

+ 1 - 1
source/putty/int64.c

@@ -116,7 +116,7 @@ uint64 uint64_shift_left(uint64 x, int shift)
     return x;
 }
 
-uint64 uint64_from_decimal(char *str)
+uint64 uint64_from_decimal(const char *str)
 {
     uint64 ret;
     ret.hi = ret.lo = 0;

+ 1 - 1
source/putty/int64.h

@@ -21,7 +21,7 @@ uint64 uint64_subtract(uint64 x, uint64 y);
 double uint64_to_double(uint64 x);
 uint64 uint64_shift_right(uint64 x, int shift);
 uint64 uint64_shift_left(uint64 x, int shift);
-uint64 uint64_from_decimal(char *str);
+uint64 uint64_from_decimal(const char *str);
 
 void BinarySink_put_uint64(BinarySink *, uint64);
 uint64 BinarySource_get_uint64(BinarySource *);

+ 10 - 0
source/putty/mainchan.c

@@ -37,6 +37,16 @@ static const struct ChannelVtable mainchan_channelvt = {
     mainchan_rcvd_exit_status,
     mainchan_rcvd_exit_signal,
     mainchan_rcvd_exit_signal_numeric,
+    chan_no_run_shell,
+    chan_no_run_command,
+    chan_no_run_subsystem,
+    chan_no_enable_x11_forwarding,
+    chan_no_enable_agent_forwarding,
+    chan_no_allocate_pty,
+    chan_no_set_env,
+    chan_no_send_break,
+    chan_no_send_signal,
+    chan_no_change_window_size,
     mainchan_request_response,
 };
 

+ 130 - 19
source/putty/portfwd.c

@@ -454,21 +454,23 @@ static const struct ChannelVtable PortForwarding_channelvt = {
     chan_no_exit_status,
     chan_no_exit_signal,
     chan_no_exit_signal_numeric,
+    chan_no_run_shell,
+    chan_no_run_command,
+    chan_no_run_subsystem,
+    chan_no_enable_x11_forwarding,
+    chan_no_enable_agent_forwarding,
+    chan_no_allocate_pty,
+    chan_no_set_env,
+    chan_no_send_break,
+    chan_no_send_signal,
+    chan_no_change_window_size,
     chan_no_request_response,
 };
 
-/*
- called when someone connects to the local port
- */
-
-static int pfl_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx)
+Channel *portfwd_raw_new(ConnectionLayer *cl, Plug **plug)
 {
     struct PortForwarding *pf;
-    struct PortListener *pl;
-    Socket *s;
-    const char *err;
 
-    pl = container_of(p, struct PortListener, plug);
     pf = new_portfwd_state();
     pf->plug.vt = &PortForwarding_plugvt;
     pf->chan.initial_fixed_window_size = 0;
@@ -476,29 +478,72 @@ static int pfl_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx)
     pf->input_wanted = TRUE;
 
     pf->c = NULL;
-    pf->cl = pl->cl;
 
-    pf->s = s = constructor(ctx, &pf->plug);
+    pf->cl = cl;
+    pf->input_wanted = TRUE;
+    pf->ready = 0;
+
+    pf->socks_state = SOCKS_NONE;
+    pf->hostname = NULL;
+    pf->port = 0;
+
+    *plug = &pf->plug;
+    return &pf->chan;
+}
+
+void portfwd_raw_free(Channel *pfchan)
+{
+    struct PortForwarding *pf;
+    assert(pfchan->vt == &PortForwarding_channelvt);
+    pf = container_of(pfchan, struct PortForwarding, chan);
+    free_portfwd_state(pf);
+}
+
+void portfwd_raw_setup(Channel *pfchan, Socket *s, SshChannel *sc)
+{
+    struct PortForwarding *pf;
+    assert(pfchan->vt == &PortForwarding_channelvt);
+    pf = container_of(pfchan, struct PortForwarding, chan);
+
+    pf->s = s;
+    pf->c = sc;
+}
+
+/*
+ called when someone connects to the local port
+ */
+
+static int pfl_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx)
+{
+    struct PortListener *pl = container_of(p, struct PortListener, plug);
+    struct PortForwarding *pf;
+    Channel *chan;
+    Plug *plug;
+    Socket *s;
+    const char *err;
+
+    chan = portfwd_raw_new(pl->cl, &plug);
+    s = constructor(ctx, plug);
     if ((err = sk_socket_error(s)) != NULL) {
-	free_portfwd_state(pf);
-	return err != NULL;
+	portfwd_raw_free(chan);
+	return TRUE;
     }
 
-    pf->input_wanted = TRUE;
-    pf->ready = 0;
+    pf = container_of(chan, struct PortForwarding, chan);
 
     if (pl->is_dynamic) {
+        pf->s = s;
 	pf->socks_state = SOCKS_INITIAL;
         pf->socksbuf = strbuf_new();
         pf->socksbuf_consumed = 0;
 	pf->port = 0;		       /* "hostname" buffer is so far empty */
 	sk_set_frozen(s, 0);	       /* we want to receive SOCKS _now_! */
     } else {
-	pf->socks_state = SOCKS_NONE;
 	pf->hostname = dupstr(pl->hostname);
 	pf->port = pl->port;	
-        pf->c = wrap_lportfwd_open(pl->cl, pf->hostname, pf->port,
-                                   s, &pf->chan);
+        portfwd_raw_setup(
+            chan, s,
+            wrap_lportfwd_open(pl->cl, pf->hostname, pf->port, s, &pf->chan));
     }
 
     return 0;
@@ -635,7 +680,7 @@ static void pfd_open_failure(Channel *chan, const char *errtext)
     PortForwarding *pf = container_of(chan, PortForwarding, chan);
 
     logeventf(pf->cl->logctx,
-              "Forwarded connection refused by server%s%s",
+              "Forwarded connection refused by remote%s%s",
               errtext ? ": " : "", errtext ? errtext : "");
 }
 
@@ -1007,6 +1052,72 @@ void portfwdmgr_config(PortFwdManager *mgr, Conf *conf)
     }
 }
 
+int portfwdmgr_listen(PortFwdManager *mgr, const char *host, int port,
+                      const char *keyhost, int keyport, Conf *conf)
+{
+    PortFwdRecord *pfr;
+
+    pfr = snew(PortFwdRecord);
+    pfr->type = 'L';
+    pfr->saddr = host ? dupstr(host) : NULL;
+    pfr->daddr = keyhost ? dupstr(keyhost) : NULL;
+    pfr->sserv = pfr->dserv = NULL;
+    pfr->sport = port;
+    pfr->dport = keyport;
+    pfr->local = NULL;
+    pfr->remote = NULL;
+    pfr->addressfamily = ADDRTYPE_UNSPEC;
+
+    PortFwdRecord *existing = add234(mgr->forwardings, pfr);
+    if (existing != pfr) {
+        /*
+         * We had this record already. Return failure.
+         */
+        pfr_free(pfr);
+        return FALSE;
+    }
+
+    char *err = pfl_listen(keyhost, keyport, host, port,
+                           mgr->cl, conf, &pfr->local, pfr->addressfamily);
+    logeventf(mgr->cl->logctx,
+              "%s on port %s:%d to forward to client%s%s",
+              err ? "Failed to listen" : "Listening", host, port,
+              err ? ": " : "", err ? err : "");
+    if (err) {
+        sfree(err);
+        del234(mgr->forwardings, pfr);
+        pfr_free(pfr);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+int portfwdmgr_unlisten(PortFwdManager *mgr, const char *host, int port)
+{
+    PortFwdRecord pfr_key;
+
+    pfr_key.type = 'L';
+    /* Safe to cast the const away here, because it will only be used
+     * by pfr_cmp, which won't write to the string */
+    pfr_key.saddr = pfr_key.daddr = (char *)host;
+    pfr_key.sserv = pfr_key.dserv = NULL;
+    pfr_key.sport = pfr_key.dport = port;
+    pfr_key.local = NULL;
+    pfr_key.remote = NULL;
+    pfr_key.addressfamily = ADDRTYPE_UNSPEC;
+
+    PortFwdRecord *pfr = del234(mgr->forwardings, &pfr_key);
+
+    if (!pfr)
+        return FALSE;
+
+    logeventf(mgr->cl->logctx, "Closing listening port %s:%d", host, port);
+
+    pfr_free(pfr);
+    return TRUE;
+}
+
 /*
  * Called when receiving a PORT OPEN from the server to make a
  * connection to a destination host.

+ 1 - 4
source/putty/putty.h

@@ -1020,9 +1020,6 @@ void do_text(Context, int, int, wchar_t *, int, unsigned long, int,
 void do_cursor(Context, int, int, wchar_t *, int, unsigned long, int,
                truecolour);
 int char_width(Context ctx, int uc);
-#ifdef OPTIMISE_SCROLL
-void do_scroll(Context, int, int, int);
-#endif
 void set_title(Frontend *frontend, char *);
 void set_icon(Frontend *frontend, char *);
 void set_sbar(Frontend *frontend, int, int, int);
@@ -1043,7 +1040,6 @@ void modalfatalbox(const char *, ...);
 void do_beep(Frontend *frontend, int);
 void sys_cursor(Frontend *frontend, int x, int y);
 void frontend_request_paste(Frontend *frontend, int clipboard);
-#define OPTIMISE_IS_SCROLL 1
 
 void set_iconic(Frontend *frontend, int iconic);
 void move_window(Frontend *frontend, int x, int y);
@@ -1685,6 +1681,7 @@ int agent_exists(void);
  * Exports from wildcard.c
  */
 const char *wc_error(int value);
+int wc_match_pl(const char *wildcard, ptrlen target);
 int wc_match(const char *wildcard, const char *target);
 int wc_unescape(char *output, const char *wildcard);
 

+ 4 - 3
source/putty/ssh.c

@@ -180,7 +180,7 @@ static void ssh_got_ssh_version(struct ssh_version_receiver *rcv,
             int is_simple =
                 (conf_get_int(ssh->conf, CONF_ssh_simple) && !ssh->connshare);
 
-            ssh->bpp = ssh2_bpp_new(ssh->logctx, &ssh->stats);
+            ssh->bpp = ssh2_bpp_new(ssh->logctx, &ssh->stats, FALSE);
             ssh_connect_bpp(ssh);
 
 #ifndef NO_GSSAPI
@@ -248,7 +248,7 @@ static void ssh_got_ssh_version(struct ssh_version_receiver *rcv,
                 ssh_verstring_get_local(old_bpp),
                 ssh_verstring_get_remote(old_bpp),
                 &ssh->gss_state,
-                &ssh->stats, transport_child_layer);
+                &ssh->stats, transport_child_layer, FALSE);
             ssh_connect_ppl(ssh, ssh->base_layer);
 
             if (userauth_layer)
@@ -724,7 +724,8 @@ static const char *connect_to_host(Ssh *ssh, const char *host, int port,
     ssh->version_receiver.got_ssh_version = ssh_got_ssh_version;
     ssh->bpp = ssh_verstring_new(
         ssh->conf, ssh->logctx, ssh->bare_connection,
-        ssh->version == 1 ? "1.5" : "2.0", &ssh->version_receiver);
+        ssh->version == 1 ? "1.5" : "2.0", &ssh->version_receiver,
+        FALSE, "PuTTY");
     ssh_connect_bpp(ssh);
     queue_idempotent_callback(&ssh->bpp->ic_in_raw);
 

+ 32 - 0
source/putty/ssh.h

@@ -226,6 +226,12 @@ struct ConnectionLayerVtable {
     /* Initiate opening of a 'session'-type channel */
     SshChannel *(*session_open)(ConnectionLayer *cl, Channel *chan);
 
+    /* Open outgoing channels for X and agent forwarding. (Used in the
+     * SSH server.) */
+    SshChannel *(*serverside_x11_open)(ConnectionLayer *cl, Channel *chan,
+                                       const SocketPeerInfo *pi);
+    SshChannel *(*serverside_agent_open)(ConnectionLayer *cl, Channel *chan);
+
     /* Add an X11 display for ordinary X forwarding */
     struct X11FakeAuth *(*add_x11_display)(
         ConnectionLayer *cl, int authtype, struct X11Display *x11disp);
@@ -304,6 +310,10 @@ struct ConnectionLayer {
 #define ssh_rportfwd_remove(cl, rpf) ((cl)->vt->rportfwd_remove(cl, rpf))
 #define ssh_lportfwd_open(cl, h, p, desc, pi, chan) \
     ((cl)->vt->lportfwd_open(cl, h, p, desc, pi, chan))
+#define ssh_serverside_x11_open(cl, chan, pi) \
+    ((cl)->vt->serverside_x11_open(cl, chan, pi))
+#define ssh_serverside_agent_open(cl, chan) \
+    ((cl)->vt->serverside_agent_open(cl, chan))
 #define ssh_session_open(cl, chan) \
     ((cl)->vt->session_open(cl, chan))
 #define ssh_add_x11_display(cl, auth, disp) \
@@ -337,6 +347,8 @@ struct ConnectionLayer {
 #define ssh_enable_agent_fwd(cl) ((cl)->vt->enable_agent_fwd(cl))
 #define ssh_set_wants_user_input(cl, wanted) \
     ((cl)->vt->set_wants_user_input(cl, wanted))
+#define ssh_setup_server_x_forwarding(cl, conf, ap, ad, scr) \
+    ((cl)->vt->setup_server_x_forwarding(cl, conf, ap, ad, scr))
 
 /* Exports from portfwd.c */
 PortFwdManager *portfwdmgr_new(ConnectionLayer *cl);
@@ -347,6 +359,15 @@ void portfwdmgr_close_all(PortFwdManager *mgr);
 char *portfwdmgr_connect(PortFwdManager *mgr, Channel **chan_ret,
                          char *hostname, int port, SshChannel *c,
                          int addressfamily);
+int portfwdmgr_listen(PortFwdManager *mgr, const char *host, int port,
+                      const char *keyhost, int keyport, Conf *conf);
+int portfwdmgr_unlisten(PortFwdManager *mgr, const char *host, int port);
+Channel *portfwd_raw_new(ConnectionLayer *cl, Plug **plug);
+void portfwd_raw_free(Channel *pfchan);
+void portfwd_raw_setup(Channel *pfchan, Socket *s, SshChannel *sc);
+
+Socket *platform_make_agent_socket(Plug *plug, const char *dirprefix,
+                                   char **error, char **name);
 
 LogContext *ssh_get_logctx(Ssh *ssh);
 
@@ -479,6 +500,7 @@ void BinarySource_get_rsa_ssh1_priv(
     BinarySource *src, struct RSAKey *rsa);
 int rsa_ssh1_encrypt(unsigned char *data, int length, struct RSAKey *key);
 Bignum rsa_ssh1_decrypt(Bignum input, struct RSAKey *key);
+int rsa_ssh1_decrypt_pkcs1(Bignum input, struct RSAKey *key, strbuf *outbuf);
 void rsasanitise(struct RSAKey *key);
 int rsastr_len(struct RSAKey *key);
 void rsastr_fmt(char *str, struct RSAKey *key);
@@ -507,12 +529,17 @@ int detect_attack(struct crcda_ctx *ctx, unsigned char *buf, uint32 len,
  * SSH2 RSA key exchange functions
  */
 struct ssh_hashalg;
+struct ssh_rsa_kex_extra {
+    int minklen;
+};
 struct RSAKey *ssh_rsakex_newkey(const void *data, int len);
 void ssh_rsakex_freekey(struct RSAKey *key);
 int ssh_rsakex_klen(struct RSAKey *key);
 void ssh_rsakex_encrypt(const struct ssh_hashalg *h,
                         unsigned char *in, int inlen,
                         unsigned char *out, int outlen, struct RSAKey *key);
+Bignum ssh_rsakex_decrypt(const struct ssh_hashalg *h, ptrlen ciphertext,
+                          struct RSAKey *rsa);
 
 /*
  * SSH2 ECDH key exchange functions
@@ -1021,6 +1048,9 @@ char *platform_get_x_display(void);
  */
 void x11_get_auth_from_authfile(struct X11Display *display,
 				const char *authfilename);
+void x11_format_auth_for_authfile(
+    BinarySink *bs, SockAddr *addr, int display_no,
+    ptrlen authproto, ptrlen authdata);
 int x11_identify_auth_proto(ptrlen protoname);
 void *x11_dehexify(ptrlen hex, int *outlen);
 
@@ -1456,6 +1486,8 @@ struct ssh_ttymodes {
 };
 
 struct ssh_ttymodes get_ttymodes_from_conf(Seat *seat, Conf *conf);
+struct ssh_ttymodes read_ttymodes_from_packet(
+    BinarySource *bs, int ssh_version);
 void write_ttymodes_to_packet(BinarySink *bs, int ssh_version,
                               struct ssh_ttymodes modes);
 

+ 19 - 10
source/putty/ssh1bpp.c

@@ -89,6 +89,21 @@ void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
     }
 }
 
+void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp)
+{
+    struct ssh1_bpp_state *s;
+    assert(bpp->vt == &ssh1_bpp_vtable);
+    s = container_of(bpp, struct ssh1_bpp_state, bpp);
+
+    assert(!s->compctx);
+    assert(!s->decompctx);
+
+    s->compctx = ssh_compressor_new(&ssh_zlib);
+    s->decompctx = ssh_decompressor_new(&ssh_zlib);
+
+    bpp_logevent(("Started zlib (RFC1950) compression"));
+}
+
 #define BPP_READ(ptr, len) do                                   \
     {                                                           \
         crMaybeWaitUntilV(s->bpp.input_eof ||                   \
@@ -116,7 +131,7 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
 
         if (s->len < 0 || s->len > 262144) { /* SSH1.5-mandated max size */
             ssh_sw_abort(s->bpp.ssh,
-                         "Extremely large packet length from server suggests"
+                         "Extremely large packet length from remote suggests"
                          " data stream corruption");
             crStopV;
         }
@@ -221,13 +236,7 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
                          * If the response was positive, start
                          * compression.
                          */
-                        assert(!s->compctx);
-                        assert(!s->decompctx);
-
-                        s->compctx = ssh_compressor_new(&ssh_zlib);
-                        s->decompctx = ssh_decompressor_new(&ssh_zlib);
-
-                        bpp_logevent(("Started zlib (RFC1950) compression"));
+                        ssh1_bpp_start_compression(&s->bpp);
                     }
 
                     /*
@@ -246,9 +255,9 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
   eof:
     if (!s->bpp.expect_close) {
         ssh_remote_error(s->bpp.ssh,
-                         "Server unexpectedly closed network connection");
+                         "Remote side unexpectedly closed network connection");
     } else {
-        ssh_remote_eof(s->bpp.ssh, "Server closed network connection");
+        ssh_remote_eof(s->bpp.ssh, "Remote side closed network connection");
     }
     return;  /* avoid touching s now it's been freed */
 

+ 18 - 1
source/putty/ssh1connection-client.c

@@ -400,7 +400,8 @@ static void ssh1mainchan_hint_channel_is_simple(SshChannel *sc)
 {
 }
 
-static int ssh1mainchan_write(SshChannel *sc, const void *data, int len)
+static int ssh1mainchan_write(
+    SshChannel *sc, int is_stderr, const void *data, int len)
 {
     struct ssh1_connection_state *s =
         container_of(sc, struct ssh1_connection_state, mainchan_sc);
@@ -431,6 +432,9 @@ static const struct SshChannelVtable ssh1mainchan_vtable = {
     NULL /* get_conf */,
     NULL /* window_override_removed is only used by SSH-2 sharing */,
     NULL /* x11_sharing_handover, likewise */,
+    NULL /* send_exit_status */,
+    NULL /* send_exit_signal */,
+    NULL /* send_exit_signal_numeric */,
     ssh1mainchan_request_x11_forwarding,
     ssh1mainchan_request_agent_forwarding,
     ssh1mainchan_request_pty,
@@ -519,3 +523,16 @@ struct ssh_rportfwd *ssh1_rportfwd_alloc(
 
     return rpf;
 }
+
+SshChannel *ssh1_serverside_x11_open(
+    ConnectionLayer *cl, Channel *chan, const SocketPeerInfo *pi)
+{
+    assert(FALSE && "Should never be called in the client");
+    return NULL;
+}
+
+SshChannel *ssh1_serverside_agent_open(ConnectionLayer *cl, Channel *chan)
+{
+    assert(FALSE && "Should never be called in the client");
+    return NULL;
+}

+ 13 - 4
source/putty/ssh1connection.c

@@ -72,6 +72,8 @@ static const struct ConnectionLayerVtable ssh1_connlayer_vtable = {
     ssh1_rportfwd_remove,
     ssh1_lportfwd_open,
     ssh1_session_open,
+    ssh1_serverside_x11_open,
+    ssh1_serverside_agent_open,
     ssh1_add_x11_display,
     NULL /* add_sharing_x11_display */,
     NULL /* remove_sharing_x11_display */,
@@ -92,7 +94,8 @@ static const struct ConnectionLayerVtable ssh1_connlayer_vtable = {
     ssh1_set_wants_user_input,
 };
 
-static int ssh1channel_write(SshChannel *c, const void *buf, int len);
+static int ssh1channel_write(
+    SshChannel *c, int is_stderr, const void *buf, int len);
 static void ssh1channel_write_eof(SshChannel *c);
 static void ssh1channel_initiate_close(SshChannel *c, const char *err);
 static void ssh1channel_unthrottle(SshChannel *c, int bufsize);
@@ -107,6 +110,9 @@ static const struct SshChannelVtable ssh1channel_vtable = {
     ssh1channel_get_conf,
     ssh1channel_window_override_removed,
     NULL /* x11_sharing_handover is only used by SSH-2 connection sharing */,
+    NULL /* send_exit_status */,
+    NULL /* send_exit_signal */,
+    NULL /* send_exit_signal_numeric */,
     NULL /* request_x11_forwarding */,
     NULL /* request_agent_forwarding */,
     NULL /* request_pty */,
@@ -211,12 +217,14 @@ static void ssh1_connection_free(PacketProtocolLayer *ppl)
     sfree(s);
 }
 
-void ssh1_connection_set_local_protoflags(PacketProtocolLayer *ppl, int flags)
+void ssh1_connection_set_protoflags(PacketProtocolLayer *ppl,
+                                    int local, int remote)
 {
     pinitassert(ppl->vt == &ssh1_connection_vtable);
     struct ssh1_connection_state *s =
         container_of(ppl, struct ssh1_connection_state, ppl);
-    s->local_protoflags = flags;
+    s->local_protoflags = local;
+    s->remote_protoflags = remote;
 }
 
 static int ssh1_connection_filter_queue(struct ssh1_connection_state *s)
@@ -584,7 +592,8 @@ static void ssh1channel_unthrottle(SshChannel *sc, int bufsize)
     }
 }
 
-static int ssh1channel_write(SshChannel *sc, const void *buf, int len)
+static int ssh1channel_write(
+    SshChannel *sc, int is_stderr, const void *buf, int len)
 {
     struct ssh1_channel *c = container_of(sc, struct ssh1_channel, sc);
     struct ssh1_connection_state *s = c->connlayer;

+ 6 - 1
source/putty/ssh1connection.h

@@ -8,7 +8,7 @@ struct ssh1_connection_state {
     Ssh *ssh;
 
     Conf *conf;
-    int local_protoflags;
+    int local_protoflags, remote_protoflags;
 
     tree234 *channels;		       /* indexed by local id */
 
@@ -49,6 +49,8 @@ struct ssh1_connection_state {
      */
     struct outstanding_succfail *succfail_head, *succfail_tail;
 
+    int compressing;                   /* used in server mode only */
+
     ConnectionLayer cl;
     PacketProtocolLayer ppl;
 };
@@ -105,6 +107,9 @@ struct ssh_rportfwd *ssh1_rportfwd_alloc(
     const char *shost, int sport, const char *dhost, int dport,
     int addressfamily, const char *log_description, PortFwdRecord *pfr,
     ssh_sharing_connstate *share_ctx);
+SshChannel *ssh1_serverside_x11_open(
+    ConnectionLayer *cl, Channel *chan, const SocketPeerInfo *pi);
+SshChannel *ssh1_serverside_agent_open(ConnectionLayer *cl, Channel *chan);
 
 void ssh1_connection_direction_specific_setup(
     struct ssh1_connection_state *s);

+ 5 - 6
source/putty/ssh1login.c

@@ -821,7 +821,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                 s->cur_prompt->to_server = TRUE;
                 s->cur_prompt->name = dupstr("SSH TIS authentication");
                 /* Prompt heuristic comes from OpenSSH */
-                if (memchr(challenge.ptr, '\n', challenge.len)) {
+                if (!memchr(challenge.ptr, '\n', challenge.len)) {
                     instr_suf = dupstr("");
                     prompt = mkstr(challenge);
                 } else {
@@ -842,8 +842,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                                 ssh1_pkt_type(pktin->type));
                 return;
             }
-        }
-        if (conf_get_int(s->conf, CONF_try_tis_auth) &&
+        } else if (conf_get_int(s->conf, CONF_try_tis_auth) &&
             (s->supported_auths_mask & (1 << SSH1_AUTH_CCARD)) &&
             !s->ccard_auth_refused) {
             s->pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE;
@@ -871,7 +870,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                 s->cur_prompt->name = dupstr("SSH CryptoCard authentication");
                 s->cur_prompt->name_reqd = FALSE;
                 /* Prompt heuristic comes from OpenSSH */
-                if (memchr(challenge.ptr, '\n', challenge.len)) {
+                if (!memchr(challenge.ptr, '\n', challenge.len)) {
                     instr_suf = dupstr("");
                     prompt = mkstr(challenge);
                 } else {
@@ -1086,8 +1085,8 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
 	}
     }
 
-    ssh1_connection_set_local_protoflags(
-        s->successor_layer, s->local_protoflags);
+    ssh1_connection_set_protoflags(
+        s->successor_layer, s->local_protoflags, s->remote_protoflags);
     {
         PacketProtocolLayer *successor = s->successor_layer;
         s->successor_layer = NULL;     /* avoid freeing it ourself */

+ 2 - 2
source/putty/ssh2bpp-bare.c

@@ -133,9 +133,9 @@ static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp)
   eof:
     if (!s->bpp.expect_close) {
         ssh_remote_error(s->bpp.ssh,
-                         "Server unexpectedly closed network connection");
+                         "Remote side unexpectedly closed network connection");
     } else {
-        ssh_remote_eof(s->bpp.ssh, "Server closed network connection");
+        ssh_remote_eof(s->bpp.ssh, "Remote side closed network connection");
     }
     return;  /* avoid touching s now it's been freed */
 

+ 39 - 27
source/putty/ssh2bpp.c

@@ -34,6 +34,7 @@ struct ssh2_bpp_state {
     ssh_decompressor *in_decomp;
     ssh_compressor *out_comp;
 
+    int is_server;
     int pending_newkeys;
     int pending_compression, seen_userauth_success;
 
@@ -54,13 +55,14 @@ static const struct BinaryPacketProtocolVtable ssh2_bpp_vtable = {
 };
 
 BinaryPacketProtocol *ssh2_bpp_new(
-    LogContext *logctx, struct DataTransferStats *stats)
+    LogContext *logctx, struct DataTransferStats *stats, int is_server)
 {
     struct ssh2_bpp_state *s = snew(struct ssh2_bpp_state);
     memset(s, 0, sizeof(*s));
     s->bpp.vt = &ssh2_bpp_vtable;
     s->bpp.logctx = logctx;
     s->stats = stats;
+    s->is_server = is_server;
     ssh_bpp_common_setup(&s->bpp);
     return &s->bpp;
 }
@@ -111,7 +113,7 @@ void ssh2_bpp_new_outgoing_crypto(
             (ssh2_cipher_alg(s->out.cipher)->flags & SSH_CIPHER_IS_CBC) &&
             !(s->bpp.remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE));
 
-        bpp_logevent(("Initialised %.200s client->server encryption",
+        bpp_logevent(("Initialised %.200s outbound encryption",
                       ssh2_cipher_alg(s->out.cipher)->text_name));
     } else {
         s->out.cipher = NULL;
@@ -122,8 +124,7 @@ void ssh2_bpp_new_outgoing_crypto(
         s->out.mac = ssh2_mac_new(mac, s->out.cipher);
         mac->setkey(s->out.mac, mac_key);
 
-        bpp_logevent(("Initialised %.200s client->server"
-                      " MAC algorithm%s%s",
+        bpp_logevent(("Initialised %.200s outbound MAC algorithm%s%s",
                       ssh2_mac_alg(s->out.mac)->text_name,
                       etm_mode ? " (in ETM mode)" : "",
                       (s->out.cipher &&
@@ -175,7 +176,7 @@ void ssh2_bpp_new_incoming_crypto(
         ssh2_cipher_setkey(s->in.cipher, ckey);
         ssh2_cipher_setiv(s->in.cipher, iv);
 
-        bpp_logevent(("Initialised %.200s server->client encryption",
+        bpp_logevent(("Initialised %.200s inbound encryption",
                       ssh2_cipher_alg(s->in.cipher)->text_name));
     } else {
         s->in.cipher = NULL;
@@ -185,7 +186,7 @@ void ssh2_bpp_new_incoming_crypto(
         s->in.mac = ssh2_mac_new(mac, s->in.cipher);
         mac->setkey(s->in.mac, mac_key);
 
-        bpp_logevent(("Initialised %.200s server->client MAC algorithm%s%s",
+        bpp_logevent(("Initialised %.200s inbound MAC algorithm%s%s",
                       ssh2_mac_alg(s->in.mac)->text_name,
                       etm_mode ? " (in ETM mode)" : "",
                       (s->in.cipher &&
@@ -217,6 +218,10 @@ void ssh2_bpp_new_incoming_crypto(
     /* Clear the pending_newkeys flag, so that handle_input below will
      * start consuming the input data again. */
     s->pending_newkeys = FALSE;
+
+    /* And schedule a run of handle_input, in case there's already
+     * input data in the queue. */
+    queue_idempotent_callback(&s->bpp.ic_in_raw);
 }
 
 int ssh2_bpp_rekey_inadvisable(BinaryPacketProtocol *bpp)
@@ -228,6 +233,24 @@ int ssh2_bpp_rekey_inadvisable(BinaryPacketProtocol *bpp)
     return s->pending_compression;
 }
 
+static void ssh2_bpp_enable_pending_compression(struct ssh2_bpp_state *s)
+{
+    BinaryPacketProtocol *bpp = &s->bpp; /* for bpp_logevent */
+
+    if (s->in.pending_compression) {
+        s->in_decomp = ssh_decompressor_new(s->in.pending_compression);
+        bpp_logevent(("Initialised delayed %s decompression",
+                      ssh_decompressor_alg(s->in_decomp)->text_name));
+        s->in.pending_compression = NULL;
+    }
+    if (s->out.pending_compression) {
+        s->out_comp = ssh_compressor_new(s->out.pending_compression);
+        bpp_logevent(("Initialised delayed %s compression",
+                      ssh_compressor_alg(s->out_comp)->text_name));
+        s->out.pending_compression = NULL;
+    }
+}
+
 #define BPP_READ(ptr, len) do                                   \
     {                                                           \
         crMaybeWaitUntilV(s->bpp.input_eof ||                   \
@@ -572,29 +595,14 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
                 continue;
             }
 
-            if (type == SSH2_MSG_USERAUTH_SUCCESS) {
+            if (type == SSH2_MSG_USERAUTH_SUCCESS && !s->is_server) {
                 /*
                  * Another one: if we were configured with OpenSSH's
                  * deferred compression which is triggered on receipt
                  * of USERAUTH_SUCCESS, then this is the moment to
                  * turn on compression.
                  */
-                if (s->in.pending_compression) {
-                    s->in_decomp =
-                        ssh_decompressor_new(s->in.pending_compression);
-                    bpp_logevent(("Initialised delayed %s decompression",
-                                  ssh_decompressor_alg(
-                                      s->in_decomp)->text_name));
-                    s->in.pending_compression = NULL;
-                }
-                if (s->out.pending_compression) {
-                    s->out_comp =
-                        ssh_compressor_new(s->out.pending_compression);
-                    bpp_logevent(("Initialised delayed %s compression",
-                                  ssh_compressor_alg(
-                                      s->out_comp)->text_name));
-                    s->out.pending_compression = NULL;
-                }
+                ssh2_bpp_enable_pending_compression(s);
 
                 /*
                  * Whether or not we were doing delayed compression in
@@ -628,9 +636,9 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
   eof:
     if (!s->bpp.expect_close) {
         ssh_remote_error(s->bpp.ssh,
-                         "Server unexpectedly closed network connection");
+                         "Remote side unexpectedly closed network connection");
     } else {
-        ssh_remote_eof(s->bpp.ssh, "Server closed network connection");
+        ssh_remote_eof(s->bpp.ssh, "Remote side closed network connection");
     }
     return;  /* avoid touching s now it's been freed */
 
@@ -870,13 +878,15 @@ static void ssh2_bpp_handle_output(BinaryPacketProtocol *bpp)
     }
 
     while ((pkt = pq_pop(&s->bpp.out_pq)) != NULL) {
-        if (userauth_range(pkt->type))
+        int type = pkt->type;
+
+        if (userauth_range(type))
             n_userauth--;
 
         ssh2_bpp_format_packet(s, pkt);
         ssh_free_pktout(pkt);
 
-        if (n_userauth == 0 && s->out.pending_compression) {
+        if (n_userauth == 0 && s->out.pending_compression && !s->is_server) {
             /*
              * This is the last userauth packet in the queue, so
              * unless our side decides to send another one in future,
@@ -886,6 +896,8 @@ static void ssh2_bpp_handle_output(BinaryPacketProtocol *bpp)
              */
             s->pending_compression = TRUE;
             return;
+        } else if (type == SSH2_MSG_USERAUTH_SUCCESS && s->is_server) {
+            ssh2_bpp_enable_pending_compression(s);
         }
     }
 }

+ 30 - 0
source/putty/ssh2connection-client.c

@@ -305,6 +305,19 @@ SshChannel *ssh2_session_open(ConnectionLayer *cl, Channel *chan)
     return &c->sc;
 }
 
+SshChannel *ssh2_serverside_x11_open(
+    ConnectionLayer *cl, Channel *chan, const SocketPeerInfo *pi)
+{
+    assert(FALSE && "Should never be called in the client");
+    return 0;                          /* placate optimiser */
+}
+
+SshChannel *ssh2_serverside_agent_open(ConnectionLayer *cl, Channel *chan)
+{
+    assert(FALSE && "Should never be called in the client");
+    return 0;                          /* placate optimiser */
+}
+
 static void ssh2_channel_response(
     struct ssh2_channel *c, PktIn *pkt, void *ctx)
 {
@@ -347,6 +360,23 @@ int ssh2channel_start_subsystem(
     return TRUE;
 }
 
+void ssh2channel_send_exit_status(SshChannel *sc, int status)
+{
+    assert(FALSE && "Should never be called in the client");
+}
+
+void ssh2channel_send_exit_signal(
+    SshChannel *sc, ptrlen signame, int core_dumped, ptrlen msg)
+{
+    assert(FALSE && "Should never be called in the client");
+}
+
+void ssh2channel_send_exit_signal_numeric(
+    SshChannel *sc, int signum, int core_dumped, ptrlen msg)
+{
+    assert(FALSE && "Should never be called in the client");
+}
+
 void ssh2channel_request_x11_forwarding(
     SshChannel *sc, int want_reply, const char *authproto,
     const char *authdata, int screen_number, int oneshot)

+ 95 - 11
source/putty/ssh2connection.c

@@ -73,6 +73,8 @@ static const struct ConnectionLayerVtable ssh2_connlayer_vtable = {
     ssh2_rportfwd_remove,
     ssh2_lportfwd_open,
     ssh2_session_open,
+    ssh2_serverside_x11_open,
+    ssh2_serverside_agent_open,
     ssh2_add_x11_display,
     ssh2_add_sharing_x11_display,
     ssh2_remove_sharing_x11_display,
@@ -120,7 +122,8 @@ static char *ssh2_channel_open_failure_error_text(PktIn *pktin)
     return dupprintf("%s [%.*s]", reason_code_string, PTRLEN_PRINTF(reason));
 }
 
-static int ssh2channel_write(SshChannel *c, const void *buf, int len);
+static int ssh2channel_write(
+    SshChannel *c, int is_stderr, const void *buf, int len);
 static void ssh2channel_write_eof(SshChannel *c);
 static void ssh2channel_initiate_close(SshChannel *c, const char *err);
 static void ssh2channel_unthrottle(SshChannel *c, int bufsize);
@@ -140,6 +143,9 @@ static const struct SshChannelVtable ssh2channel_vtable = {
     ssh2channel_get_conf,
     ssh2channel_window_override_removed,
     ssh2channel_x11_sharing_handover,
+    ssh2channel_send_exit_status,
+    ssh2channel_send_exit_signal,
+    ssh2channel_send_exit_signal_numeric,
     ssh2channel_request_x11_forwarding,
     ssh2channel_request_agent_forwarding,
     ssh2channel_request_pty,
@@ -219,6 +225,7 @@ struct outstanding_channel_request {
 static void ssh2_channel_free(struct ssh2_channel *c)
 {
     bufchain_clear(&c->outbuffer);
+    bufchain_clear(&c->errbuffer);
     while (c->chanreq_head) {
         struct outstanding_channel_request *chanreq = c->chanreq_head;
         c->chanreq_head = c->chanreq_head->next;
@@ -657,6 +664,66 @@ static int ssh2_connection_filter_queue(struct ssh2_connection_state *s)
                         reply_success = FALSE;
                         break;
                     }
+                } else if (ptrlen_eq_string(type, "shell")) {
+                    reply_success = chan_run_shell(c->chan);
+                } else if (ptrlen_eq_string(type, "exec")) {
+                    ptrlen command = get_string(pktin);
+                    reply_success = chan_run_command(c->chan, command);
+                } else if (ptrlen_eq_string(type, "subsystem")) {
+                    ptrlen subsys = get_string(pktin);
+                    reply_success = chan_run_subsystem(c->chan, subsys);
+                } else if (ptrlen_eq_string(type, "x11-req")) {
+                    int oneshot = get_bool(pktin);
+                    ptrlen authproto = get_string(pktin);
+                    ptrlen authdata = get_string(pktin);
+                    unsigned screen_number = get_uint32(pktin);
+                    reply_success = chan_enable_x11_forwarding(
+                        c->chan, oneshot, authproto, authdata, screen_number);
+                } else if (ptrlen_eq_string(type,
+                                            "[email protected]")) {
+                    reply_success = chan_enable_agent_forwarding(c->chan);
+                } else if (ptrlen_eq_string(type, "pty-req")) {
+                    ptrlen termtype = get_string(pktin);
+                    unsigned width = get_uint32(pktin);
+                    unsigned height = get_uint32(pktin);
+                    unsigned pixwidth = get_uint32(pktin);
+                    unsigned pixheight = get_uint32(pktin);
+                    ptrlen encoded_modes = get_string(pktin);
+                    BinarySource bs_modes[1];
+                    struct ssh_ttymodes modes;
+
+                    BinarySource_BARE_INIT(
+                        bs_modes, encoded_modes.ptr, encoded_modes.len);
+                    modes = read_ttymodes_from_packet(bs_modes, 2);
+                    if (get_err(bs_modes) || get_avail(bs_modes) > 0) {
+                        ppl_logevent(("Unable to decode terminal mode "
+                                      "string"));
+                        reply_success = FALSE;
+                    } else {
+                        reply_success = chan_allocate_pty(
+                            c->chan, termtype, width, height,
+                            pixwidth, pixheight, modes);
+                    }
+                } else if (ptrlen_eq_string(type, "env")) {
+                    ptrlen var = get_string(pktin);
+                    ptrlen value = get_string(pktin);
+
+                    reply_success = chan_set_env(c->chan, var, value);
+                } else if (ptrlen_eq_string(type, "break")) {
+                    unsigned length = get_uint32(pktin);
+
+                    reply_success = chan_send_break(c->chan, length);
+                } else if (ptrlen_eq_string(type, "signal")) {
+                    ptrlen signame = get_string(pktin);
+
+                    reply_success = chan_send_signal(c->chan, signame);
+                } else if (ptrlen_eq_string(type, "window-change")) {
+                    unsigned width = get_uint32(pktin);
+                    unsigned height = get_uint32(pktin);
+                    unsigned pixwidth = get_uint32(pktin);
+                    unsigned pixheight = get_uint32(pktin);
+                    reply_success = chan_change_window_size(
+                        c->chan, width, height, pixwidth, pixheight);
                 }
                 if (want_reply) {
                     int type = (reply_success ? SSH2_MSG_CHANNEL_SUCCESS :
@@ -745,6 +812,7 @@ static int ssh2_connection_filter_queue(struct ssh2_connection_state *s)
                      * stuff.
                      */
                     bufchain_clear(&c->outbuffer);
+                    bufchain_clear(&c->errbuffer);
 
                     /*
                      * Send outgoing EOF.
@@ -989,7 +1057,7 @@ static void ssh2_channel_try_eof(struct ssh2_channel *c)
     assert(c->pending_eof);          /* precondition for calling us */
     if (c->halfopen)
         return;                 /* can't close: not even opened yet */
-    if (bufchain_size(&c->outbuffer) > 0)
+    if (bufchain_size(&c->outbuffer) > 0 || bufchain_size(&c->errbuffer) > 0)
         return;              /* can't send EOF: pending outgoing data */
 
     c->pending_eof = FALSE;            /* we're about to send it */
@@ -1010,19 +1078,31 @@ static int ssh2_try_send(struct ssh2_channel *c)
     PktOut *pktout;
     int bufsize;
 
-    while (c->remwindow > 0 && bufchain_size(&c->outbuffer) > 0) {
+    while (c->remwindow > 0 &&
+           (bufchain_size(&c->outbuffer) > 0 ||
+            bufchain_size(&c->errbuffer) > 0)) {
 	int len;
 	void *data;
-	bufchain_prefix(&c->outbuffer, &data, &len);
+        bufchain *buf = (bufchain_size(&c->errbuffer) > 0 ?
+                         &c->errbuffer : &c->outbuffer);
+
+	bufchain_prefix(buf, &data, &len);
 	if ((unsigned)len > c->remwindow)
 	    len = c->remwindow;
 	if ((unsigned)len > c->remmaxpkt)
 	    len = c->remmaxpkt;
-	pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_CHANNEL_DATA);
-	put_uint32(pktout, c->remoteid);
+        if (buf == &c->errbuffer) {
+            pktout = ssh_bpp_new_pktout(
+                s->ppl.bpp, SSH2_MSG_CHANNEL_EXTENDED_DATA);
+            put_uint32(pktout, c->remoteid);
+            put_uint32(pktout, SSH2_EXTENDED_DATA_STDERR);
+        } else {
+            pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_CHANNEL_DATA);
+            put_uint32(pktout, c->remoteid);
+        }
         put_string(pktout, data, len);
         pq_push(s->ppl.out_pq, pktout);
-	bufchain_consume(&c->outbuffer, len);
+	bufchain_consume(buf, len);
 	c->remwindow -= len;
     }
 
@@ -1030,7 +1110,7 @@ static int ssh2_try_send(struct ssh2_channel *c)
      * After having sent as much data as we can, return the amount
      * still buffered.
      */
-    bufsize = bufchain_size(&c->outbuffer);
+    bufsize = bufchain_size(&c->outbuffer) + bufchain_size(&c->errbuffer);
 
     /*
      * And if there's no data pending but we need to send an EOF, send
@@ -1158,12 +1238,14 @@ void ssh2_channel_init(struct ssh2_channel *c)
     c->closes = 0;
     c->pending_eof = FALSE;
     c->throttling_conn = FALSE;
+    c->throttled_by_backlog = FALSE;
     c->sharectx = NULL;
     c->locwindow = c->locmaxwin = c->remlocwin =
         s->ssh_is_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE;
     c->chanreq_head = NULL;
     c->throttle_state = UNTHROTTLED;
     bufchain_init(&c->outbuffer);
+    bufchain_init(&c->errbuffer);
     c->sc.vt = &ssh2channel_vtable;
     c->sc.cl = &s->cl;
     c->localid = alloc_channel_id(s->channels, struct ssh2_channel);
@@ -1273,11 +1355,12 @@ static void ssh2channel_unthrottle(SshChannel *sc, int bufsize)
     }
 }
 
-static int ssh2channel_write(SshChannel *sc, const void *buf, int len)
+static int ssh2channel_write(
+    SshChannel *sc, int is_stderr, const void *buf, int len)
 {
     struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
     assert(!(c->closes & CLOSES_SENT_EOF));
-    bufchain_add(&c->outbuffer, buf, len);
+    bufchain_add(is_stderr ? &c->errbuffer : &c->outbuffer, buf, len);
     return ssh2_try_send(c);
 }
 
@@ -1530,7 +1613,8 @@ static int ssh2_stdin_backlog(ConnectionLayer *cl)
     if (!s->mainchan)
         return 0;
     c = container_of(s->mainchan_sc, struct ssh2_channel, sc);
-    return s->mainchan ? bufchain_size(&c->outbuffer) : 0;
+    return s->mainchan ?
+        bufchain_size(&c->outbuffer) + bufchain_size(&c->errbuffer) : 0;
 }
 
 static void ssh2_throttle_all_channels(ConnectionLayer *cl, int throttled)

+ 12 - 3
source/putty/ssh2connection.h

@@ -38,6 +38,8 @@ struct ssh2_connection_state {
     PortFwdManager *portfwdmgr;
     int portfwdmgr_configured;
 
+    const SftpServerVtable *sftpserver_vt;
+
     /*
      * These store the list of global requests that we're waiting for
      * replies to. (REQUEST_FAILURE doesn't come with any indication
@@ -95,7 +97,7 @@ struct ssh2_channel {
      */
     int throttled_by_backlog;
 
-    bufchain outbuffer;
+    bufchain outbuffer, errbuffer;
     unsigned remwindow, remmaxpkt;
     /* locwindow is signed so we can cope with excess data. */
     int locwindow, locmaxwin;
@@ -164,9 +166,16 @@ struct ssh_rportfwd *ssh2_rportfwd_alloc(
     ssh_sharing_connstate *share_ctx);
 void ssh2_rportfwd_remove(
     ConnectionLayer *cl, struct ssh_rportfwd *rpf);
-
 SshChannel *ssh2_session_open(ConnectionLayer *cl, Channel *chan);
-
+SshChannel *ssh2_serverside_x11_open(
+    ConnectionLayer *cl, Channel *chan, const SocketPeerInfo *pi);
+SshChannel *ssh2_serverside_agent_open(ConnectionLayer *cl, Channel *chan);
+
+void ssh2channel_send_exit_status(SshChannel *c, int status);
+void ssh2channel_send_exit_signal(
+    SshChannel *c, ptrlen signame, int core_dumped, ptrlen msg);
+void ssh2channel_send_exit_signal_numeric(
+    SshChannel *c, int signum, int core_dumped, ptrlen msg);
 void ssh2channel_request_x11_forwarding(
     SshChannel *c, int want_reply, const char *authproto,
     const char *authdata, int screen_number, int oneshot);

+ 98 - 43
source/putty/ssh2transport.c

@@ -110,8 +110,8 @@ PacketProtocolLayer *ssh2_transport_new(
     Conf *conf, const char *host, int port, const char *fullhostname,
     const char *client_greeting, const char *server_greeting,
     struct ssh_connection_shared_gss_state *shgss,
-    struct DataTransferStats *stats,
-    PacketProtocolLayer *higher_layer)
+    struct DataTransferStats *stats, PacketProtocolLayer *higher_layer,
+    int is_server)
 {
     struct ssh2_transport_state *s = snew(struct ssh2_transport_state);
     memset(s, 0, sizeof(*s));
@@ -125,6 +125,7 @@ PacketProtocolLayer *ssh2_transport_new(
     s->client_greeting = dupstr(client_greeting);
     s->server_greeting = dupstr(server_greeting);
     s->stats = stats;
+    s->hostkeyblob = strbuf_new();
 
     pq_in_init(&s->pq_in_higher, higher_layer->seat); // WINSCP
     pq_out_init(&s->pq_out_higher, higher_layer->seat); // WINSCP
@@ -145,8 +146,17 @@ PacketProtocolLayer *ssh2_transport_new(
     s->thc = ssh_transient_hostkey_cache_new();
     s->gss_kex_used = FALSE;
 
-    s->client_kexinit = strbuf_new();
-    s->server_kexinit = strbuf_new();
+    s->outgoing_kexinit = strbuf_new();
+    s->incoming_kexinit = strbuf_new();
+    if (is_server) {
+        s->client_kexinit = s->incoming_kexinit;
+        s->server_kexinit = s->outgoing_kexinit;
+        s->out.mkkey_adjust = 1;
+    } else {
+        s->client_kexinit = s->outgoing_kexinit;
+        s->server_kexinit = s->incoming_kexinit;
+        s->in.mkkey_adjust = 1;
+    }
 
     ssh2_transport_set_max_data_size(s);
 
@@ -187,8 +197,9 @@ static void ssh2_transport_free(PacketProtocolLayer *ppl)
     sfree(s->server_greeting);
     sfree(s->keystr);
     sfree(s->hostkey_str);
+    strbuf_free(s->hostkeyblob);
     sfree(s->fingerprint);
-    if (s->hkey) {
+    if (s->hkey && !s->hostkeys) {
         ssh_key_free(s->hkey);
         s->hkey = NULL;
     }
@@ -204,8 +215,8 @@ static void ssh2_transport_free(PacketProtocolLayer *ppl)
         ssh_ecdhkex_freekey(s->ecdh_key);
     if (s->exhash)
         ssh_hash_free(s->exhash);
-    strbuf_free(s->client_kexinit);
-    strbuf_free(s->server_kexinit);
+    strbuf_free(s->outgoing_kexinit);
+    strbuf_free(s->incoming_kexinit);
     ssh_transient_hostkey_cache_free(s->thc);
     sfree(s);
 }
@@ -324,7 +335,7 @@ int ssh2_common_filter_queue(PacketProtocolLayer *ppl)
             msg = get_string(pktin);
 
             ssh_remote_error(
-                ppl->ssh, "Server sent disconnect message\n"
+                ppl->ssh, "Remote side sent disconnect message\n"
                 "type %d (%s):\n\"%.*s\"", reason,
                 ((reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ?
                  ssh2_disconnect_reasons[reason] : "unknown"),
@@ -400,6 +411,7 @@ static void ssh2_write_kexinit_lists(
     Conf *conf, int remote_bugs,
     const char *hk_host, int hk_port, const ssh_keyalg *hk_prev,
     ssh_transient_hostkey_cache *thc,
+    ssh_key *const *our_hostkeys, int our_nhostkeys,
     int first_time, int can_gssapi_keyex, int transient_hostkey_mode)
 {
     int i, j, k, warn;
@@ -527,7 +539,18 @@ static void ssh2_write_kexinit_lists(
             }
     }
     /* List server host key algorithms. */
-    if (first_time) {
+    if (our_hostkeys) {
+        /*
+         * In server mode, we just list the algorithms that match the
+         * host keys we actually have.
+         */
+        for (i = 0; i < our_nhostkeys; i++) {
+            alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY],
+                                      ssh_key_alg(our_hostkeys[i])->ssh_id);
+            alg->u.hk.hostkey = ssh_key_alg(our_hostkeys[i]);
+            alg->u.hk.warn = FALSE;
+        }
+    } else if (first_time) {
         /*
          * In the first key exchange, we list all the algorithms
          * we're prepared to cope with, but prefer those algorithms
@@ -1021,28 +1044,29 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
      * later.
      */
     s->client_kexinit->len = 0;
-    put_byte(s->client_kexinit, SSH2_MSG_KEXINIT);
+    put_byte(s->outgoing_kexinit, SSH2_MSG_KEXINIT);
     {
         int i;
         for (i = 0; i < 16; i++)
-            put_byte(s->client_kexinit, (unsigned char) random_byte());
+            put_byte(s->outgoing_kexinit, (unsigned char) random_byte());
     }
     ssh2_write_kexinit_lists(
-        /*WINSCP*/ s->ppl.seat, BinarySink_UPCAST(s->client_kexinit), s->kexlists,
+        /*WINSCP*/ s->ppl.seat, BinarySink_UPCAST(s->outgoing_kexinit), s->kexlists,
         s->conf, s->ppl.remote_bugs,
         s->savedhost, s->savedport, s->hostkey_alg, s->thc,
+        s->hostkeys, s->nhostkeys,
         !s->got_session_id, s->can_gssapi_keyex,
         s->gss_kex_used && !s->need_gss_transient_hostkey);
     /* First KEX packet does _not_ follow, because we're not that brave. */
-    put_bool(s->client_kexinit, FALSE);
-    put_uint32(s->client_kexinit, 0);             /* reserved */
+    put_bool(s->outgoing_kexinit, FALSE);
+    put_uint32(s->outgoing_kexinit, 0);             /* reserved */
 
     /*
      * Send our KEXINIT.
      */
     pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXINIT);
-    put_data(pktout, s->client_kexinit->u + 1,
-             s->client_kexinit->len - 1); /* omit initial packet type byte */
+    put_data(pktout, s->outgoing_kexinit->u + 1,
+             s->outgoing_kexinit->len - 1); /* omit initial packet type byte */
     pq_push(s->ppl.out_pq, pktout);
 
     /*
@@ -1061,9 +1085,9 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
                                       s->ppl.bpp->pls->actx, pktin->type));
         return;
     }
-    s->server_kexinit->len = 0;
-    put_byte(s->server_kexinit, SSH2_MSG_KEXINIT);
-    put_data(s->server_kexinit, get_ptr(pktin), get_avail(pktin));
+    s->incoming_kexinit->len = 0;
+    put_byte(s->incoming_kexinit, SSH2_MSG_KEXINIT);
+    put_data(s->incoming_kexinit, get_ptr(pktin), get_avail(pktin));
 
     /*
      * Work through the two KEXINIT packets in parallel to find the
@@ -1246,8 +1270,8 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
     ssh_bpp_handle_output(s->ppl.bpp);
 
     /*
-     * We've sent client NEWKEYS, so create and initialise
-     * client-to-server session keys.
+     * We've sent outgoing NEWKEYS, so create and initialise outgoing
+     * session keys.
      */
     {
         strbuf *cipher_key = strbuf_new();
@@ -1255,14 +1279,15 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
         strbuf *mac_key = strbuf_new();
 
         if (s->out.cipher) {
-            ssh2_mkkey(s, cipher_iv, s->K, s->exchange_hash, 'A',
-                       s->out.cipher->blksize);
-            ssh2_mkkey(s, cipher_key, s->K, s->exchange_hash, 'C',
+            ssh2_mkkey(s, cipher_iv, s->K, s->exchange_hash,
+                       'A' + s->out.mkkey_adjust, s->out.cipher->blksize);
+            ssh2_mkkey(s, cipher_key, s->K, s->exchange_hash,
+                       'C' + s->out.mkkey_adjust,
                        s->out.cipher->padded_keybytes);
         }
         if (s->out.mac) {
-            ssh2_mkkey(s, mac_key, s->K, s->exchange_hash, 'E',
-                       s->out.mac->keylen);
+            ssh2_mkkey(s, mac_key, s->K, s->exchange_hash,
+                       'E' + s->out.mkkey_adjust, s->out.mac->keylen);
         }
 
         ssh2_bpp_new_outgoing_crypto(
@@ -1301,8 +1326,8 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
     s->stats->in.remaining = s->max_data_size;
 
     /*
-     * We've seen server NEWKEYS, so create and initialise
-     * server-to-client session keys.
+     * We've seen incoming NEWKEYS, so create and initialise
+     * incoming session keys.
      */
     {
         strbuf *cipher_key = strbuf_new();
@@ -1310,14 +1335,15 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
         strbuf *mac_key = strbuf_new();
 
         if (s->in.cipher) {
-            ssh2_mkkey(s, cipher_iv, s->K, s->exchange_hash, 'B',
-                       s->in.cipher->blksize);
-            ssh2_mkkey(s, cipher_key, s->K, s->exchange_hash, 'D',
+            ssh2_mkkey(s, cipher_iv, s->K, s->exchange_hash,
+                       'A' + s->in.mkkey_adjust, s->in.cipher->blksize);
+            ssh2_mkkey(s, cipher_key, s->K, s->exchange_hash,
+                       'C' + s->in.mkkey_adjust,
                        s->in.cipher->padded_keybytes);
         }
         if (s->in.mac) {
-            ssh2_mkkey(s, mac_key, s->K, s->exchange_hash, 'F',
-                       s->in.mac->keylen);
+            ssh2_mkkey(s, mac_key, s->K, s->exchange_hash,
+                       'E' + s->in.mkkey_adjust, s->in.mac->keylen);
         }
 
         ssh2_bpp_new_incoming_crypto(
@@ -1369,14 +1395,43 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
      * decided to initiate a rekey ourselves for some reason.
      */
     if (!s->higher_layer_ok) {
-        pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_SERVICE_REQUEST);
-        put_stringz(pktout, s->higher_layer->vt->name);
-        pq_push(s->ppl.out_pq, pktout);
-        crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
-        if (pktin->type != SSH2_MSG_SERVICE_ACCEPT) {
-            ssh_sw_abort(s->ppl.ssh, "Server refused request to start "
-                         "'%s' protocol", s->higher_layer->vt->name);
-            return;
+        if (!s->hostkeys) {
+            /* We're the client, so send SERVICE_REQUEST. */
+            pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_SERVICE_REQUEST);
+            put_stringz(pktout, s->higher_layer->vt->name);
+            pq_push(s->ppl.out_pq, pktout);
+            crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
+            if (pktin->type != SSH2_MSG_SERVICE_ACCEPT) {
+                ssh_sw_abort(s->ppl.ssh, "Server refused request to start "
+                             "'%s' protocol", s->higher_layer->vt->name);
+                return;
+            }
+        } else {
+            ptrlen service_name;
+
+            /* We're the server, so expect SERVICE_REQUEST. */
+            crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
+            if (pktin->type != SSH2_MSG_SERVICE_REQUEST) {
+                ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
+                                "expecting SERVICE_REQUEST, type %d (%s)",
+                                pktin->type,
+                                ssh2_pkt_type(s->ppl.bpp->pls->kctx,
+                                              s->ppl.bpp->pls->actx,
+                                              pktin->type));
+                return;
+            }
+            service_name = get_string(pktin);
+            if (!ptrlen_eq_string(service_name, s->higher_layer->vt->name)) {
+                ssh_proto_error(s->ppl.ssh, "Client requested service "
+                                "'%.*s' when we only support '%s'",
+                                PTRLEN_PRINTF(service_name),
+                                s->higher_layer->vt->name);
+                return;
+            }
+
+            pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_SERVICE_ACCEPT);
+            put_stringz(pktout, s->higher_layer->vt->name);
+            pq_push(s->ppl.out_pq, pktout);
         }
 
         s->higher_layer_ok = TRUE;
@@ -1405,7 +1460,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
                 return;
             }
             pq_push_front(s->ppl.in_pq, pktin);
-            ppl_logevent(("Server initiated key re-exchange"));
+            ppl_logevent(("Remote side initiated key re-exchange"));
             s->rekey_class = RK_SERVER;
         }
 
@@ -1450,7 +1505,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
              * rekey, we process it anyway!)
              */
             if ((s->ppl.remote_bugs & BUG_SSH2_REKEY)) {
-                ppl_logevent(("Server bug prevents key re-exchange (%s)",
+                ppl_logevent(("Remote bug prevents key re-exchange (%s)",
                               s->rekey_reason));
                 /* Reset the counters, so that at least this message doesn't
                  * hit the event log _too_ often. */

+ 8 - 1
source/putty/ssh2transport.h

@@ -109,6 +109,7 @@ typedef struct transport_direction {
     int etm_mode;
     const struct ssh_compression_alg *comp;
     int comp_delayed;
+    int mkkey_adjust;
 } transport_direction;
 
 struct ssh2_transport_state {
@@ -132,6 +133,7 @@ struct ssh2_transport_state {
     char *hostkey_str; /* string representation, for easy checking in rekeys */
     unsigned char session_id[SSH2_KEX_MAX_HASH_LEN];
     int session_id_len;
+    int dh_min_size, dh_max_size, dh_got_size_bounds;
     struct dh_ctx *dh_ctx;
     ssh_hash *exhash;
 
@@ -164,10 +166,12 @@ struct ssh2_transport_state {
 
     int nbits, pbits, warn_kex, warn_hk, warn_cscipher, warn_sccipher;
     Bignum p, g, e, f, K;
-    strbuf *client_kexinit, *server_kexinit;
+    strbuf *outgoing_kexinit, *incoming_kexinit;
+    strbuf *client_kexinit, *server_kexinit; /* aliases to the above */
     int kex_init_value, kex_reply_value;
     transport_direction in, out;
     ptrlen hostkeydata, sigdata;
+    strbuf *hostkeyblob;
     char *keystr, *fingerprint;
     ssh_key *hkey;                     /* actual host key */
     struct RSAKey *rsa_kex_key;             /* for RSA kex */
@@ -204,6 +208,9 @@ struct ssh2_transport_state {
      */
     int cross_certifying;
 
+    ssh_key *const *hostkeys;
+    int nhostkeys;
+
     PacketProtocolLayer ppl;
 };
 

+ 2 - 1
source/putty/ssh2userauth.c

@@ -1,5 +1,6 @@
 /*
- * Packet protocol layer for the SSH-2 userauth protocol (RFC 4252).
+ * Packet protocol layer for the client side of the SSH-2 userauth
+ * protocol (RFC 4252).
  */
 
 #include <assert.h>

+ 7 - 2
source/putty/sshbpp.h

@@ -56,6 +56,10 @@ BinaryPacketProtocol *ssh1_bpp_new(LogContext *logctx);
 void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
                          const struct ssh1_cipheralg *cipher,
                          const void *session_key);
+/* This is only called from outside the BPP in server mode; in client
+ * mode the BPP detects compression start time automatically by
+ * snooping message types */
+void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp);
 
 /* Helper routine which does common BPP initialisation, e.g. setting
  * up in_pq and out_pq, and initialising input_consumer. */
@@ -99,7 +103,7 @@ struct DataTransferStats {
      ((stats)->direction.remaining -= (size), FALSE))
 
 BinaryPacketProtocol *ssh2_bpp_new(
-    LogContext *logctx, struct DataTransferStats *stats);
+    LogContext *logctx, struct DataTransferStats *stats, int is_server);
 void ssh2_bpp_new_outgoing_crypto(
     BinaryPacketProtocol *bpp,
     const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,
@@ -136,7 +140,8 @@ struct ssh_version_receiver {
 };
 BinaryPacketProtocol *ssh_verstring_new(
     Conf *conf, LogContext *logctx, int bare_connection_mode,
-    const char *protoversion, struct ssh_version_receiver *rcv);
+    const char *protoversion, struct ssh_version_receiver *rcv,
+    int server_mode, const char *impl_name);
 const char *ssh_verstring_get_remote(BinaryPacketProtocol *);
 const char *ssh_verstring_get_local(BinaryPacketProtocol *);
 int ssh_verstring_get_bugs(BinaryPacketProtocol *);

+ 67 - 2
source/putty/sshchan.h

@@ -34,6 +34,22 @@ struct ChannelVtable {
         Channel *chan, ptrlen signame, int core_dumped, ptrlen msg);
     int (*rcvd_exit_signal_numeric)(
         Channel *chan, int signum, int core_dumped, ptrlen msg);
+    int (*run_shell)(Channel *chan);
+    int (*run_command)(Channel *chan, ptrlen command);
+    int (*run_subsystem)(Channel *chan, ptrlen subsys);
+    int (*enable_x11_forwarding)(
+        Channel *chan, int oneshot, ptrlen authproto, ptrlen authdata,
+        unsigned screen_number);
+    int (*enable_agent_forwarding)(Channel *chan);
+    int (*allocate_pty)(
+        Channel *chan, ptrlen termtype, unsigned width, unsigned height,
+        unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes);
+    int (*set_env)(Channel *chan, ptrlen var, ptrlen value);
+    int (*send_break)(Channel *chan, unsigned length);
+    int (*send_signal)(Channel *chan, ptrlen signame);
+    int (*change_window_size)(
+        Channel *chan, unsigned width, unsigned height,
+        unsigned pixwidth, unsigned pixheight);
 
     /* A method for signalling success/failure responses to channel
      * requests initiated from the SshChannel vtable with want_reply
@@ -61,6 +77,26 @@ struct Channel {
     ((ch)->vt->rcvd_exit_signal(ch, sig, core, msg))
 #define chan_rcvd_exit_signal_numeric(ch, sig, core, msg)   \
     ((ch)->vt->rcvd_exit_signal_numeric(ch, sig, core, msg))
+#define chan_run_shell(ch) \
+    ((ch)->vt->run_shell(ch))
+#define chan_run_command(ch, cmd) \
+    ((ch)->vt->run_command(ch, cmd))
+#define chan_run_subsystem(ch, subsys) \
+    ((ch)->vt->run_subsystem(ch, subsys))
+#define chan_enable_x11_forwarding(ch, oneshot, ap, ad, scr) \
+    ((ch)->vt->enable_x11_forwarding(ch, oneshot, ap, ad, scr))
+#define chan_enable_agent_forwarding(ch) \
+    ((ch)->vt->enable_agent_forwarding(ch))
+#define chan_allocate_pty(ch, termtype, w, h, pw, ph, modes) \
+    ((ch)->vt->allocate_pty(ch, termtype, w, h, pw, ph, modes))
+#define chan_set_env(ch, var, value) \
+    ((ch)->vt->set_env(ch, var, value))
+#define chan_send_break(ch, length) \
+    ((ch)->vt->send_break(ch, length))
+#define chan_send_signal(ch, signame) \
+    ((ch)->vt->send_signal(ch, signame))
+#define chan_change_window_size(ch, w, h, pw, ph) \
+    ((ch)->vt->change_window_size(ch, w, h, pw, ph))
 #define chan_request_response(ch, success)   \
     ((ch)->vt->request_response(ch, success))
 
@@ -81,6 +117,22 @@ int chan_default_want_close(Channel *, int, int);
 int chan_no_exit_status(Channel *, int);
 int chan_no_exit_signal(Channel *, ptrlen, int, ptrlen);
 int chan_no_exit_signal_numeric(Channel *, int, int, ptrlen);
+int chan_no_run_shell(Channel *chan);
+int chan_no_run_command(Channel *chan, ptrlen command);
+int chan_no_run_subsystem(Channel *chan, ptrlen subsys);
+int chan_no_enable_x11_forwarding(
+    Channel *chan, int oneshot, ptrlen authproto, ptrlen authdata,
+    unsigned screen_number);
+int chan_no_enable_agent_forwarding(Channel *chan);
+int chan_no_allocate_pty(
+    Channel *chan, ptrlen termtype, unsigned width, unsigned height,
+    unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes);
+int chan_no_set_env(Channel *chan, ptrlen var, ptrlen value);
+int chan_no_send_break(Channel *chan, unsigned length);
+int chan_no_send_signal(Channel *chan, ptrlen signame);
+int chan_no_change_window_size(
+    Channel *chan, unsigned width, unsigned height,
+    unsigned pixwidth, unsigned pixheight);
 
 /* default implementation that never expects to receive a response */
 void chan_no_request_response(Channel *, int);
@@ -104,7 +156,7 @@ Channel *zombiechan_new(void);
  */
 
 struct SshChannelVtable {
-    int (*write)(SshChannel *c, const void *, int);
+    int (*write)(SshChannel *c, int is_stderr, const void *, int);
     void (*write_eof)(SshChannel *c);
     void (*initiate_close)(SshChannel *c, const char *err);
     void (*unthrottle)(SshChannel *c, int bufsize);
@@ -131,6 +183,11 @@ struct SshChannelVtable {
      * wouldn't do anything usefully different with the reply in any
      * case.)
      */
+    void (*send_exit_status)(SshChannel *c, int status);
+    void (*send_exit_signal)(
+        SshChannel *c, ptrlen signame, int core_dumped, ptrlen msg);
+    void (*send_exit_signal_numeric)(
+        SshChannel *c, int signum, int core_dumped, ptrlen msg);
     void (*request_x11_forwarding)(
         SshChannel *c, int want_reply, const char *authproto,
         const char *authdata, int screen_number, int oneshot);
@@ -160,7 +217,9 @@ struct SshChannel {
     ConnectionLayer *cl;
 };
 
-#define sshfwd_write(c, buf, len) ((c)->vt->write(c, buf, len))
+#define sshfwd_write(c, buf, len) ((c)->vt->write(c, FALSE, buf, len))
+#define sshfwd_write_ext(c, stderr, buf, len) \
+    ((c)->vt->write(c, stderr, buf, len))
 #define sshfwd_write_eof(c) ((c)->vt->write_eof(c))
 #define sshfwd_initiate_close(c, err) ((c)->vt->initiate_close(c, err))
 #define sshfwd_unthrottle(c, bufsize) ((c)->vt->unthrottle(c, bufsize))
@@ -168,6 +227,12 @@ struct SshChannel {
 #define sshfwd_window_override_removed(c) ((c)->vt->window_override_removed(c))
 #define sshfwd_x11_sharing_handover(c, cs, ch, pa, pp, e, pmaj, pmin, d, l) \
     ((c)->vt->x11_sharing_handover(c, cs, ch, pa, pp, e, pmaj, pmin, d, l))
+#define sshfwd_send_exit_status(c, status) \
+    ((c)->vt->send_exit_status(c, status))
+#define sshfwd_send_exit_signal(c, sig, core, msg) \
+    ((c)->vt->send_exit_signal(c, sig, core, msg))
+#define sshfwd_send_exit_signal_numeric(c, sig, core, msg) \
+    ((c)->vt->send_exit_signal_numeric(c, sig, core, msg))
 #define sshfwd_request_x11_forwarding(c, wr, ap, ad, scr, oneshot) \
     ((c)->vt->request_x11_forwarding(c, wr, ap, ad, scr, oneshot))
 #define sshfwd_request_agent_forwarding(c, wr) \

+ 132 - 0
source/putty/sshcommon.c

@@ -306,6 +306,16 @@ static const struct ChannelVtable zombiechan_channelvt = {
     chan_no_exit_status,
     chan_no_exit_signal,
     chan_no_exit_signal_numeric,
+    chan_no_run_shell,
+    chan_no_run_command,
+    chan_no_run_subsystem,
+    chan_no_enable_x11_forwarding,
+    chan_no_enable_agent_forwarding,
+    chan_no_allocate_pty,
+    chan_no_set_env,
+    chan_no_send_break,
+    chan_no_send_signal,
+    chan_no_change_window_size,
     chan_no_request_response,
 };
 
@@ -392,6 +402,62 @@ int chan_no_exit_signal_numeric(
     return FALSE;
 }
 
+int chan_no_run_shell(Channel *chan)
+{
+    return FALSE;
+}
+
+int chan_no_run_command(Channel *chan, ptrlen command)
+{
+    return FALSE;
+}
+
+int chan_no_run_subsystem(Channel *chan, ptrlen subsys)
+{
+    return FALSE;
+}
+
+int chan_no_enable_x11_forwarding(
+    Channel *chan, int oneshot, ptrlen authproto, ptrlen authdata,
+    unsigned screen_number)
+{
+    return FALSE;
+}
+
+int chan_no_enable_agent_forwarding(Channel *chan)
+{
+    return FALSE;
+}
+
+int chan_no_allocate_pty(
+    Channel *chan, ptrlen termtype, unsigned width, unsigned height,
+    unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes)
+{
+    return FALSE;
+}
+
+int chan_no_set_env(Channel *chan, ptrlen var, ptrlen value)
+{
+    return FALSE;
+}
+
+int chan_no_send_break(Channel *chan, unsigned length)
+{
+    return FALSE;
+}
+
+int chan_no_send_signal(Channel *chan, ptrlen signame)
+{
+    return FALSE;
+}
+
+int chan_no_change_window_size(
+    Channel *chan, unsigned width, unsigned height,
+    unsigned pixwidth, unsigned pixheight)
+{
+    return FALSE;
+}
+
 void chan_no_request_response(Channel *chan, int success)
 {
     assert(0 && "this channel type should never send a want-reply request");
@@ -413,6 +479,29 @@ static unsigned real_ttymode_opcode(unsigned our_opcode, int ssh_version)
     }
 }
 
+static unsigned our_ttymode_opcode(unsigned real_opcode, int ssh_version)
+{
+    if (ssh_version == 1) {
+        switch (real_opcode) {
+          case TTYMODE_ISPEED_SSH1:
+            return TTYMODE_ISPEED;
+          case TTYMODE_OSPEED_SSH1:
+            return TTYMODE_OSPEED;
+          default:
+            return real_opcode;
+        }
+    } else {
+        switch (real_opcode) {
+          case TTYMODE_ISPEED_SSH2:
+            return TTYMODE_ISPEED;
+          case TTYMODE_OSPEED_SSH2:
+            return TTYMODE_OSPEED;
+          default:
+            return real_opcode;
+        }
+    }
+}
+
 struct ssh_ttymodes get_ttymodes_from_conf(Seat *seat, Conf *conf)
 {
     struct ssh_ttymodes modes;
@@ -518,6 +607,49 @@ struct ssh_ttymodes get_ttymodes_from_conf(Seat *seat, Conf *conf)
     return modes;
 }
 
+struct ssh_ttymodes read_ttymodes_from_packet(
+    BinarySource *bs, int ssh_version)
+{
+    struct ssh_ttymodes modes;
+    memset(&modes, 0, sizeof(modes));
+
+    while (1) {
+        unsigned real_opcode, our_opcode;
+
+        real_opcode = get_byte(bs);
+        if (real_opcode == TTYMODE_END_OF_LIST)
+            break;
+        if (real_opcode >= 160) {
+            /*
+             * RFC 4254 (and the SSH 1.5 spec): "Opcodes 160 to 255
+             * are not yet defined, and cause parsing to stop (they
+             * should only be used after any other data)."
+             *
+             * My interpretation of this is that if one of these
+             * opcodes appears, it's not a parse _error_, but it is
+             * something that we don't know how to parse even well
+             * enough to step over it to find the next opcode, so we
+             * stop parsing now and assume that the rest of the string
+             * is composed entirely of things we don't understand and
+             * (as usual for unsupported terminal modes) silently
+             * ignore.
+             */
+            return modes;
+        }
+
+        our_opcode = our_ttymode_opcode(real_opcode, ssh_version);
+        assert(our_opcode < TTYMODE_LIMIT);
+        modes.have_mode[our_opcode] = TRUE;
+
+        if (ssh_version == 1 && real_opcode >= 1 && real_opcode <= 127)
+            modes.mode_val[our_opcode] = get_byte(bs);
+        else
+            modes.mode_val[our_opcode] = get_uint32(bs);
+    }
+
+    return modes;
+}
+
 void write_ttymodes_to_packet(BinarySink *bs, int ssh_version,
                               struct ssh_ttymodes modes)
 {

+ 8 - 3
source/putty/sshppl.h

@@ -99,8 +99,8 @@ PacketProtocolLayer *ssh2_transport_new(
     Conf *conf, const char *host, int port, const char *fullhostname,
     const char *client_greeting, const char *server_greeting,
     struct ssh_connection_shared_gss_state *shgss,
-    struct DataTransferStats *stats,
-    PacketProtocolLayer *higher_layer);
+    struct DataTransferStats *stats, PacketProtocolLayer *higher_layer,
+    int is_server);
 PacketProtocolLayer *ssh2_userauth_new(
     PacketProtocolLayer *successor_layer,
     const char *hostname, const char *fullhostname,
@@ -139,7 +139,8 @@ ptrlen ssh2_transport_get_session_id(PacketProtocolLayer *ssh2_transport_ptr);
 void ssh2_transport_notify_auth_done(PacketProtocolLayer *ssh2_transport_ptr);
 
 /* Methods for ssh1login to pass protocol flags to ssh1connection */
-void ssh1_connection_set_local_protoflags(PacketProtocolLayer *ppl, int flags);
+void ssh1_connection_set_protoflags(
+    PacketProtocolLayer *ppl, int local, int remote);
 
 /* Shared get_specials method between the two ssh1 layers */
 int ssh1_common_get_specials(PacketProtocolLayer *, add_special_fn_t, void *);
@@ -150,4 +151,8 @@ void ssh1_compute_session_id(
     unsigned char *session_id, const unsigned char *cookie,
     struct RSAKey *hostkey, struct RSAKey *servkey);
 
+/* Method used by the SSH server */
+void ssh2_transport_provide_hostkeys(PacketProtocolLayer *ssh2_transport_ptr,
+                                     ssh_key *const *hostkeys, int nhostkeys);
+
 #endif /* PUTTY_SSHPPL_H */

+ 114 - 2
source/putty/sshrsa.c

@@ -285,6 +285,42 @@ Bignum rsa_ssh1_decrypt(Bignum input, struct RSAKey *key)
     return rsa_privkey_op(input, key);
 }
 
+int rsa_ssh1_decrypt_pkcs1(Bignum input, struct RSAKey *key, strbuf *outbuf)
+{
+    strbuf *data = strbuf_new();
+    int success = FALSE;
+    BinarySource src[1];
+
+    {
+        Bignum *b = rsa_ssh1_decrypt(input, key);
+        int i;
+        for (i = (bignum_bitcount(key->modulus) + 7) / 8; i-- > 0 ;) {
+            put_byte(data, bignum_byte(b, i));
+        }
+        freebn(b);
+    }
+
+    BinarySource_BARE_INIT(src, data->u, data->len);
+
+    /* Check PKCS#1 formatting prefix */
+    if (get_byte(src) != 0) goto out;
+    if (get_byte(src) != 2) goto out;
+    while (1) {
+        unsigned char byte = get_byte(src);
+        if (get_err(src)) goto out;
+        if (byte == 0)
+            break;
+    }
+
+    /* Everything else is the payload */
+    success = TRUE;
+    put_data(outbuf, get_ptr(src), get_avail(src));
+
+  out:
+    strbuf_free(data);
+    return success;
+}
+
 int rsastr_len(struct RSAKey *key)
 {
     Bignum md, ex;
@@ -903,12 +939,88 @@ void ssh_rsakex_encrypt(const struct ssh_hashalg *h,
      */
 }
 
+Bignum ssh_rsakex_decrypt(const struct ssh_hashalg *h, ptrlen ciphertext,
+                          struct RSAKey *rsa)
+{
+    Bignum b1, b2;
+    int outlen, i;
+    unsigned char *out;
+    unsigned char labelhash[64];
+    ssh_hash *hash;
+    BinarySource src[1];
+    const int HLEN = h->hlen;
+
+    /*
+     * Decryption side of the RSA key exchange operation.
+     */
+
+    /* The length of the encrypted data should be exactly the length
+     * in octets of the RSA modulus.. */
+    outlen = (7 + bignum_bitcount(rsa->modulus)) / 8;
+    if (ciphertext.len != outlen)
+        return NULL;
+
+    /* Do the RSA decryption, and extract the result into a byte array. */
+    b1 = bignum_from_bytes(ciphertext.ptr, ciphertext.len);
+    b2 = rsa_privkey_op(b1, rsa);
+    out = snewn(outlen, unsigned char);
+    for (i = 0; i < outlen; i++)
+        out[i] = bignum_byte(b2, outlen-1-i);
+    freebn(b1);
+    freebn(b2);
+
+    /* Do the OAEP masking operations, in the reverse order from encryption */
+    oaep_mask(h, out+HLEN+1, outlen-HLEN-1, out+1, HLEN);
+    oaep_mask(h, out+1, HLEN, out+HLEN+1, outlen-HLEN-1);
+
+    /* Check the leading byte is zero. */
+    if (out[0] != 0) {
+        sfree(out);
+        return NULL;
+    }
+    /* Check the label hash at position 1+HLEN */
+    assert(HLEN <= lenof(labelhash));
+    hash = ssh_hash_new(h);
+    ssh_hash_final(hash, labelhash);
+    if (memcmp(out + HLEN + 1, labelhash, HLEN)) {
+        sfree(out);
+        return NULL;
+    }
+    /* Expect zero bytes followed by a 1 byte */
+    for (i = 1 + 2 * HLEN; i < outlen; i++) {
+        if (out[i] == 1) {
+            i++;  /* skip over the 1 byte */
+            break;
+        } else if (out[i] != 1) {
+            sfree(out);
+            return NULL;
+        }
+    }
+    /* And what's left is the input message data, which should be
+     * encoded as an ordinary SSH-2 mpint. */
+    BinarySource_BARE_INIT(src, out + i, outlen - i);
+    b1 = get_mp_ssh2(src);
+    sfree(out);
+    if (get_err(src) || get_avail(src) != 0) {
+        freebn(b1);
+        return NULL;
+    }
+
+    /* Success! */
+    return b1;
+}
+
+static const struct ssh_rsa_kex_extra ssh_rsa_kex_extra_sha1 = { 1024 };
+static const struct ssh_rsa_kex_extra ssh_rsa_kex_extra_sha256 = { 2048 };
+
 static const struct ssh_kex ssh_rsa_kex_sha1 = {
-    "rsa1024-sha1", NULL, KEXTYPE_RSA, &ssh_sha1, NULL,
+    "rsa1024-sha1", NULL, KEXTYPE_RSA,
+    &ssh_sha1, &ssh_rsa_kex_extra_sha1,
 };
 
 static const struct ssh_kex ssh_rsa_kex_sha256 = {
-    "rsa2048-sha256", NULL, KEXTYPE_RSA, &ssh_sha256, NULL,
+    "rsa2048-sha256", NULL, KEXTYPE_RSA,
+    &ssh_sha256, &ssh_rsa_kex_extra_sha256,
 };
 
 static const struct ssh_kex *const rsa_kex_list[] = {

+ 11 - 5
source/putty/sshverstring.c

@@ -27,6 +27,7 @@ struct ssh_verstring_state {
     int major_protoversion;
     int remote_bugs;
     char prefix[PREFIX_MAXLEN];
+    char *impl_name;
     char *vstring;
     int vslen, vstrsize;
     char *protoversion;
@@ -59,7 +60,8 @@ static int ssh_version_includes_v2(const char *ver);
 
 BinaryPacketProtocol *ssh_verstring_new(
     Conf *conf, LogContext *logctx, int bare_connection_mode,
-    const char *protoversion, struct ssh_version_receiver *rcv)
+    const char *protoversion, struct ssh_version_receiver *rcv,
+    int server_mode, const char *impl_name)
 {
     struct ssh_verstring_state *s = snew(struct ssh_verstring_state);
 
@@ -90,13 +92,16 @@ BinaryPacketProtocol *ssh_verstring_new(
     s->bpp.logctx = logctx;
     s->our_protoversion = dupstr(protoversion);
     s->receiver = rcv;
+    s->impl_name = dupstr(impl_name);
 
     /*
      * We send our version string early if we can. But if it includes
      * SSH-1, we can't, because we have to take the other end into
      * account too (see below).
+     *
+     * In server mode, we do send early.
      */
-    s->send_early = !ssh_version_includes_v1(protoversion);
+    s->send_early = server_mode || !ssh_version_includes_v1(protoversion);
 
     s->bpp.vt = &ssh_verstring_vtable;
     ssh_bpp_common_setup(&s->bpp);
@@ -108,6 +113,7 @@ void ssh_verstring_free(BinaryPacketProtocol *bpp)
     struct ssh_verstring_state *s =
         container_of(bpp, struct ssh_verstring_state, bpp);
     conf_free(s->conf);
+    sfree(s->impl_name);
     sfree(s->vstring);
     sfree(s->protoversion);
     sfree(s->our_vstring);
@@ -155,9 +161,9 @@ static void ssh_verstring_send(struct ssh_verstring_state *s)
      * Construct our outgoing version string.
      */
     s->our_vstring = dupprintf(
-        "%.*s%s-%s",
+        "%.*s%s-%s%s",
         (int)s->prefix_wanted.len, (const char *)s->prefix_wanted.ptr,
-        s->our_protoversion, sshver);
+        s->our_protoversion, s->impl_name, sshver);
     sv_pos = s->prefix_wanted.len + strlen(s->our_protoversion) + 1;
 
     /* Convert minus signs and spaces in the software version string
@@ -392,7 +398,7 @@ void ssh_verstring_handle_input(BinaryPacketProtocol *bpp)
 
   eof:
     ssh_remote_error(s->bpp.ssh,
-                     "Server unexpectedly closed network connection");
+                     "Remote side unexpectedly closed network connection");
     return;  /* avoid touching s now it's been freed */
 
     crFinishV;

+ 1 - 1
source/putty/version.h

@@ -9,7 +9,7 @@
  */
 
 #define TEXTVER "Unidentified build"
-#define SSHVER "PuTTY-Unidentified-Local-Build"
+#define SSHVER "-Unidentified-Local-Build"
 #define BINARY_VERSION 0,0,0,0
 
 #ifndef SOURCE_COMMIT

+ 23 - 10
source/putty/wildcard.c

@@ -100,7 +100,8 @@ const char *wc_error(int value)
  * returns zero. If the wildcard fragment suffers a syntax error,
  * it returns <0 and the precise value indexes into wc_error.
  */
-static int wc_match_fragment(const char **fragment, const char **target)
+static int wc_match_fragment(const char **fragment, const char **target,
+                             const char *target_end)
 {
     const char *f, *t;
 
@@ -110,7 +111,7 @@ static int wc_match_fragment(const char **fragment, const char **target)
      * The fragment terminates at either the end of the string, or
      * the first (unescaped) *.
      */
-    while (*f && *f != '*' && *t) {
+    while (*f && *f != '*' && t < target_end) {
 	/*
 	 * Extract one character from t, and one character's worth
 	 * of pattern from f, and step along both. Return 0 if they
@@ -207,8 +208,10 @@ static int wc_match_fragment(const char **fragment, const char **target)
  * successful match, 0 for an unsuccessful match, and <0 for a
  * syntax error in the wildcard.
  */
-int wc_match(const char *wildcard, const char *target)
+static int wc_match_inner(
+    const char *wildcard, const char *target, size_t target_len)
 {
+    const char *target_end = target + target_len;
     int ret;
 
     /*
@@ -219,7 +222,7 @@ int wc_match(const char *wildcard, const char *target)
      * routine once and give up if it fails.
      */
     if (*wildcard != '*') {
-	ret = wc_match_fragment(&wildcard, &target);
+	ret = wc_match_fragment(&wildcard, &target, target_end);
 	if (ret <= 0)
 	    return ret;		       /* pass back failure or error alike */
     }
@@ -248,12 +251,12 @@ int wc_match(const char *wildcard, const char *target)
 	while (*target) {
 	    const char *save_w = wildcard, *save_t = target;
 
-	    ret = wc_match_fragment(&wildcard, &target);
+	    ret = wc_match_fragment(&wildcard, &target, target_end);
 
 	    if (ret < 0)
 		return ret;	       /* syntax error */
 
-	    if (ret > 0 && !*wildcard && *target) {
+	    if (ret > 0 && !*wildcard && target != target_end) {
 		/*
 		 * Final special case - literally.
 		 * 
@@ -275,9 +278,9 @@ int wc_match(const char *wildcard, const char *target)
 		 * (which is why we saved `wildcard'). Then we
 		 * return whatever that operation returns.
 		 */
-		target = save_t + strlen(save_t) - (target - save_t);
+		target = target_end - (target - save_t);
 		wildcard = save_w;
-		return wc_match_fragment(&wildcard, &target);
+		return wc_match_fragment(&wildcard, &target, target_end);
 	    }
 
 	    if (ret > 0)
@@ -295,7 +298,17 @@ int wc_match(const char *wildcard, const char *target)
      * wildcard. Hence, we return 1 if and only if we are also
      * right at the end of the target.
      */
-    return (*target ? 0 : 1);
+    return target == target_end;
+}
+
+int wc_match(const char *wildcard, const char *target)
+{
+    return wc_match_inner(wildcard, target, strlen(target));
+}
+
+int wc_match_pl(const char *wildcard, ptrlen target)
+{
+    return wc_match_inner(wildcard, target.ptr, target.len);
 }
 
 /*
@@ -442,7 +455,7 @@ int main(void)
 	f = fragment_tests[i].wildcard;
 	t = fragment_tests[i].target;
 	eret = fragment_tests[i].expected_result;
-	aret = wc_match_fragment(&f, &t);
+	aret = wc_match_fragment(&f, &t, t + strlen(t));
 	if (aret != eret) {
 	    printf("failed test: /%s/ against /%s/ returned %d not %d\n",
 		   fragment_tests[i].wildcard, fragment_tests[i].target,

+ 52 - 0
source/putty/x11fwd.c

@@ -453,6 +453,15 @@ ptrlen BinarySource_get_string_xauth(BinarySource *src)
 #define get_string_xauth(src) \
     BinarySource_get_string_xauth(BinarySource_UPCAST(src))
 
+void BinarySink_put_stringpl_xauth(BinarySink *bs, ptrlen pl)
+{
+    assert((pl.len >> 16) == 0);
+    put_uint16(bs, pl.len);
+    put_data(bs, pl.ptr, pl.len);
+}
+#define put_stringpl_xauth(bs, ptrlen) \
+    BinarySink_put_stringpl_xauth(BinarySink_UPCAST(bs),ptrlen)
+
 void x11_get_auth_from_authfile(struct X11Display *disp,
 				const char *authfilename)
 {
@@ -635,6 +644,39 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
     sfree(ourhostname);
 }
 
+void x11_format_auth_for_authfile(
+    BinarySink *bs, SockAddr *addr, int display_no,
+    ptrlen authproto, ptrlen authdata)
+{
+    if (sk_address_is_special_local(addr)) {
+        char *ourhostname = get_hostname();
+        put_uint16(bs, 256); /* indicates Unix-domain socket */
+        put_stringpl_xauth(bs, ptrlen_from_asciz(ourhostname));
+        sfree(ourhostname);
+    } else if (sk_addrtype(addr) == ADDRTYPE_IPV4) {
+        char ipv4buf[4];
+        sk_addrcopy(addr, ipv4buf);
+        put_uint16(bs, 0); /* indicates IPv4 */
+        put_stringpl_xauth(bs, make_ptrlen(ipv4buf, 4));
+    } else if (sk_addrtype(addr) == ADDRTYPE_IPV6) {
+        char ipv6buf[16];
+        sk_addrcopy(addr, ipv6buf);
+        put_uint16(bs, 6); /* indicates IPv6 */
+        put_stringpl_xauth(bs, make_ptrlen(ipv6buf, 16));
+    } else {
+        assert(FALSE && "Bad address type in x11_format_auth_for_authfile");
+    }
+
+    {
+        char *numberbuf = dupprintf("%d", display_no);
+        put_stringpl_xauth(bs, ptrlen_from_asciz(numberbuf));
+        sfree(numberbuf);
+    }
+
+    put_stringpl_xauth(bs, authproto);
+    put_stringpl_xauth(bs, authdata);
+}
+
 static void x11_log(Plug *p, int type, SockAddr *addr, int port,
 		    const char *error_msg, int error_code)
 {
@@ -738,6 +780,16 @@ static const struct ChannelVtable X11Connection_channelvt = {
     chan_no_exit_status,
     chan_no_exit_signal,
     chan_no_exit_signal_numeric,
+    chan_no_run_shell,
+    chan_no_run_command,
+    chan_no_run_subsystem,
+    chan_no_enable_x11_forwarding,
+    chan_no_enable_agent_forwarding,
+    chan_no_allocate_pty,
+    chan_no_set_env,
+    chan_no_send_break,
+    chan_no_send_signal,
+    chan_no_change_window_size,
     chan_no_request_response,
 };