From 22b492c4f63473d8b56fbded39cc640178ea495b Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 16 Feb 2020 12:07:43 +0000 Subject: [PATCH] New protocol: PROT_SSHCONN, bare ssh-connection. This is the same protocol that PuTTY's connection sharing has been using for years, to communicate between the downstream and upstream PuTTYs. I'm now promoting it to be a first-class member of the protocols list: if you have a server for it, you can select it in the GUI or on the command line, and write out a saved session that specifies it. This would be completely insecure if you used it as an ordinary network protocol, of course. Not only is it non-cryptographic and wide open to eavesdropping and hijacking, but it's not even _authenticated_ - it begins after the userauth phase of SSH. So there isn't even the mild security theatre of entering an easy-to-eavesdrop password, as there is with, say, Telnet. However, that's not what I want to use it for. My aim is to use it for various specialist and niche purposes, all of which involve speaking it over an 8-bit-clean data channel that is already set up, secured and authenticated by other methods. There are lots of examples of such channels: - a userv(1) invocation - the console of a UML kernel - the stdio channels into other kinds of container, such as Docker - the 'adb shell' channel (although it seems quite hard to run a custom binary at the far end of that) - a pair of pipes between PuTTY and a Cygwin helper process - and so on. So this protocol is intended as a convenient way to get a client at one end of any those to run a shell session at the other end. Unlike other approaches, it will give you all the SSH-flavoured amenities you're already used to, like forwarding your SSH agent into the container, or forwarding selected network ports in or out of it, or letting it open a window on your X server, or doing SCP/SFTP style file transfer. Of course another way to get all those amenities would be to run an ordinary SSH server over the same channel - but this approach avoids having to manage a phony password or authentication key, or taking up your CPU time with pointless crypto. --- be_all.c | 1 + be_all_s.c | 1 + doc/config.but | 27 ++++++++++++++++++++------- putty.h | 3 ++- ssh.c | 33 +++++++++++++++++++++++++++++++-- ssh.h | 1 + ssh1connection.h | 2 -- ssh2connection-client.c | 3 ++- ssh2connection.h | 2 -- sshshare.c | 7 ++++++- 10 files changed, 64 insertions(+), 16 deletions(-) diff --git a/be_all.c b/be_all.c index 9673c798..4f272f82 100644 --- a/be_all.c +++ b/be_all.c @@ -27,5 +27,6 @@ const struct BackendVtable *const backends[] = { &telnet_backend, &rlogin_backend, &raw_backend, + &sshconn_backend, NULL }; diff --git a/be_all_s.c b/be_all_s.c index 2f140ec0..d8e24d54 100644 --- a/be_all_s.c +++ b/be_all_s.c @@ -28,5 +28,6 @@ const struct BackendVtable *const backends[] = { &rlogin_backend, &raw_backend, &serial_backend, + &sshconn_backend, NULL }; diff --git a/doc/config.but b/doc/config.but index ee179668..cdc71d7c 100644 --- a/doc/config.but +++ b/doc/config.but @@ -22,13 +22,26 @@ filled in before PuTTY can open a session at all. address}, of the server you want to connect to. \b The \q{Connection type} radio buttons let you choose what type of -connection you want to make: a \I{raw TCP connections}raw -connection, a \i{Telnet} connection, an \i{Rlogin} connection, an -\i{SSH} connection, or a connection to a local \i{serial line}. (See -\k{which-one} for a summary of the differences between SSH, Telnet -and rlogin; see \k{using-rawprot} for an explanation of \q{raw} -connections; see \k{using-serial} for information about using a -serial line.) +connection you want to make: an \i{SSH} network connection, a +connection to a local \i{serial line}, or various other kinds of +network connection. + +\lcont{ + +\b See \k{which-one} for a summary of the +differences between the network remote login protocols SSH, Telnet and +Rlogin. + +\b See \k{using-rawprot} for an explanation of \q{raw} +connections. + +\b See \k{using-serial} for information about using a serial line. + +\b The \q{Bare ssh-connection} option in the \q{Connection type} box +is experimental, for specialist uses, and servers for it are not +widely available. + +} \b The \q{Port} box lets you specify which \i{port number} on the server to connect to. If you select Telnet, Rlogin, or SSH, this box diff --git a/putty.h b/putty.h index 9e509ace..5a288988 100644 --- a/putty.h +++ b/putty.h @@ -365,7 +365,7 @@ enum { enum { /* Protocol back ends. (CONF_protocol) */ - PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH, + PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH, PROT_SSHCONN, /* PROT_SERIAL is supported on a subset of platforms, but it doesn't * hurt to define it globally. */ PROT_SERIAL, @@ -1746,6 +1746,7 @@ extern const struct BackendVtable telnet_backend; * Exports from ssh.c. */ extern const struct BackendVtable ssh_backend; +extern const struct BackendVtable sshconn_backend; /* * Exports from ldisc.c. diff --git a/ssh.c b/ssh.c index 8c7319bf..7d74ccdc 100644 --- a/ssh.c +++ b/ssh.c @@ -311,8 +311,8 @@ static void ssh_got_ssh_version(struct ssh_version_receiver *rcv, ssh_connect_bpp(ssh); connection_layer = ssh2_connection_new( - ssh, NULL, false, ssh->conf, ssh_verstring_get_remote(old_bpp), - &ssh->cl); + ssh, ssh->connshare, false, ssh->conf, + ssh_verstring_get_remote(old_bpp), &ssh->cl); ssh_connect_ppl(ssh, connection_layer); ssh->base_layer = connection_layer; } @@ -861,6 +861,11 @@ static void ssh_cache_conf_values(Ssh *ssh) ssh->pls.omit_data = conf_get_bool(ssh->conf, CONF_logomitdata); } +bool ssh_is_bare(Ssh *ssh) +{ + return ssh->backend.vt->protocol == PROT_SSHCONN; +} + /* * Called to set up the connection. * @@ -894,6 +899,8 @@ static const char *ssh_init(const BackendVtable *vt, Seat *seat, ssh->backend.vt = vt; *backend_handle = &ssh->backend; + ssh->bare_connection = (vt->protocol == PROT_SSHCONN); + ssh->seat = seat; ssh->cl_dummy.logctx = ssh->logctx = logctx; @@ -1194,3 +1201,25 @@ const struct BackendVtable ssh_backend = { PROT_SSH, 22 }; + +const struct BackendVtable sshconn_backend = { + ssh_init, + ssh_free, + ssh_reconfig, + ssh_send, + ssh_sendbuffer, + ssh_size, + ssh_special, + ssh_get_specials, + ssh_connected, + ssh_return_exitcode, + ssh_sendok, + ssh_ldisc, + ssh_provide_ldisc, + ssh_unthrottle, + ssh_cfg_info, + ssh_test_for_upstream, + "ssh-connection", "Bare ssh-connection", + PROT_SSHCONN, + 0 +}; diff --git a/ssh.h b/ssh.h index 79dc64ff..2395c325 100644 --- a/ssh.h +++ b/ssh.h @@ -399,6 +399,7 @@ void ssh_throttle_conn(Ssh *ssh, int adjust); void ssh_got_exitcode(Ssh *ssh, int status); void ssh_ldisc_update(Ssh *ssh); void ssh_got_fallback_cmd(Ssh *ssh); +bool ssh_is_bare(Ssh *ssh); /* Communications back to ssh.c from the BPP */ void ssh_conn_processed_data(Ssh *ssh); diff --git a/ssh1connection.h b/ssh1connection.h index cb9bbe96..670ba779 100644 --- a/ssh1connection.h +++ b/ssh1connection.h @@ -5,8 +5,6 @@ struct outstanding_succfail; struct ssh1_connection_state { int crState; - Ssh *ssh; - Conf *conf; int local_protoflags, remote_protoflags; diff --git a/ssh2connection-client.c b/ssh2connection-client.c index c355e475..f4121315 100644 --- a/ssh2connection-client.c +++ b/ssh2connection-client.c @@ -496,5 +496,6 @@ void ssh2channel_send_terminal_size_change(SshChannel *sc, int w, int h) bool ssh2_connection_need_antispoof_prompt(struct ssh2_connection_state *s) { - return !seat_set_trust_status(s->ppl.seat, false); + bool success = seat_set_trust_status(s->ppl.seat, false); + return (!success && !ssh_is_bare(s->ppl.ssh)); } diff --git a/ssh2connection.h b/ssh2connection.h index 69975d7f..4b82a1a7 100644 --- a/ssh2connection.h +++ b/ssh2connection.h @@ -7,8 +7,6 @@ struct outstanding_global_request; struct ssh2_connection_state { int crState; - Ssh *ssh; - ssh_sharing_state *connshare; char *peer_verstring; diff --git a/sshshare.c b/sshshare.c index 92d2dcf5..e109f873 100644 --- a/sshshare.c +++ b/sshshare.c @@ -1993,9 +1993,14 @@ static int share_listen_accepting(Plug *plug, */ char *ssh_share_sockname(const char *host, int port, Conf *conf) { - char *username = get_remote_username(conf); + char *username = NULL; char *sockname; + /* Include the username we're logging in as in the hash, unless + * we're using a protocol for which it's completely irrelevant. */ + if (conf_get_int(conf, CONF_protocol) != PROT_SSHCONN) + username = get_remote_username(conf); + if (port == 22) { if (username) sockname = dupprintf("%s@%s", username, host);