2006-04-23 18:26:03 +00:00
|
|
|
/*
|
|
|
|
* SSH backend.
|
|
|
|
*/
|
|
|
|
|
1999-01-08 13:02:13 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2000-06-22 12:18:34 +00:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <assert.h>
|
2006-08-26 09:21:52 +00:00
|
|
|
#include <limits.h>
|
2007-01-17 18:38:45 +00:00
|
|
|
#include <signal.h>
|
1999-01-08 13:02:13 +00:00
|
|
|
|
|
|
|
#include "putty.h"
|
2017-01-30 19:42:25 +00:00
|
|
|
#include "pageant.h" /* for AGENT_MAX_MSGLEN */
|
2000-09-15 10:48:42 +00:00
|
|
|
#include "tree234.h"
|
Add manual cross-certification of new host keys.
If a server offers host key algorithms that we don't have a stored key
for, they will now appear in a submenu of the Special Commands menu.
Selecting one will force a repeat key exchange with that key, and if
it succeeds, will add the new host key to the cache. The idea is that
the new key sent by the server is protected by the crypto established
in the previous key exchange, so this is just as safe as typing some
command like 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub' at the
server prompt and transcribing the results manually.
This allows switching over to newer host key algorithms if the client
has begun to support them (e.g. people using PuTTY's new ECC
functionality for the first time), or if the server has acquired a new
key (e.g. due to a server OS upgrade).
At the moment, it's only available manually, for a single host key
type at a time. Automating it is potentially controversial for
security policy reasons (what if someone doesn't agree this is what
they want in their host key cache, or doesn't want to switch over to
using whichever of the keys PuTTY would now put top of the list?), for
code plumbing reasons (chaining several of these rekeys might be more
annoying than doing one at a time) and for CPU usage reasons (rekeys
are expensive), but even so, it might turn out to be a good idea in
future.
2016-03-21 07:25:31 +00:00
|
|
|
#include "storage.h"
|
2018-05-24 09:15:36 +00:00
|
|
|
#include "marshal.h"
|
2000-06-22 12:18:34 +00:00
|
|
|
#include "ssh.h"
|
2018-06-09 08:07:18 +00:00
|
|
|
#include "sshcr.h"
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
#include "sshbpp.h"
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
#include "sshchan.h"
|
2008-12-02 18:18:32 +00:00
|
|
|
#ifndef NO_GSSAPI
|
2010-05-19 18:22:17 +00:00
|
|
|
#include "sshgssc.h"
|
2008-08-10 13:10:31 +00:00
|
|
|
#include "sshgss.h"
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
#define MIN_CTXT_LIFETIME 5 /* Avoid rekey with short lifetime (seconds) */
|
|
|
|
#define GSS_KEX_CAPABLE (1<<0) /* Can do GSS KEX */
|
|
|
|
#define GSS_CRED_UPDATED (1<<1) /* Cred updated since previous delegation */
|
|
|
|
#define GSS_CTXT_EXPIRES (1<<2) /* Context expires before next timer */
|
|
|
|
#define GSS_CTXT_MAYFAIL (1<<3) /* Context may expire during handshake */
|
2008-12-02 18:18:32 +00:00
|
|
|
#endif
|
1999-01-08 13:02:13 +00:00
|
|
|
|
2001-04-16 09:10:34 +00:00
|
|
|
static const char *const ssh2_disconnect_reasons[] = {
|
|
|
|
NULL,
|
2005-10-13 21:51:38 +00:00
|
|
|
"host not allowed to connect",
|
|
|
|
"protocol error",
|
|
|
|
"key exchange failed",
|
|
|
|
"host authentication failed",
|
2005-10-13 22:51:44 +00:00
|
|
|
"MAC error",
|
2005-10-13 21:51:38 +00:00
|
|
|
"compression error",
|
|
|
|
"service not available",
|
|
|
|
"protocol version not supported",
|
|
|
|
"host key not verifiable",
|
|
|
|
"connection lost",
|
|
|
|
"by application",
|
|
|
|
"too many connections",
|
|
|
|
"auth cancelled by user",
|
|
|
|
"no more auth methods available",
|
|
|
|
"illegal user name",
|
2001-04-16 09:10:34 +00:00
|
|
|
};
|
2000-10-26 13:10:47 +00:00
|
|
|
|
2015-04-25 09:46:53 +00:00
|
|
|
#define DH_MIN_SIZE 1024
|
|
|
|
#define DH_MAX_SIZE 8192
|
2001-10-27 10:39:54 +00:00
|
|
|
|
2018-04-14 13:48:02 +00:00
|
|
|
/* Safely convert rekey_time to unsigned long minutes */
|
|
|
|
static unsigned long rekey_mins(int rekey_time, unsigned long def)
|
|
|
|
{
|
|
|
|
if (rekey_time < 0 || rekey_time > MAX_TICK_MINS)
|
|
|
|
rekey_time = def;
|
|
|
|
return (unsigned long)rekey_time;
|
|
|
|
}
|
|
|
|
|
2001-12-14 14:57:50 +00:00
|
|
|
#define translate(x) if (type == x) return #x
|
2007-07-21 21:39:36 +00:00
|
|
|
#define translatek(x,ctx) if (type == x && (pkt_kctx == ctx)) return #x
|
|
|
|
#define translatea(x,ctx) if (type == x && (pkt_actx == ctx)) return #x
|
2018-06-09 08:07:18 +00:00
|
|
|
const char *ssh1_pkt_type(int type)
|
2001-12-14 14:57:50 +00:00
|
|
|
{
|
|
|
|
translate(SSH1_MSG_DISCONNECT);
|
|
|
|
translate(SSH1_SMSG_PUBLIC_KEY);
|
|
|
|
translate(SSH1_CMSG_SESSION_KEY);
|
|
|
|
translate(SSH1_CMSG_USER);
|
|
|
|
translate(SSH1_CMSG_AUTH_RSA);
|
|
|
|
translate(SSH1_SMSG_AUTH_RSA_CHALLENGE);
|
|
|
|
translate(SSH1_CMSG_AUTH_RSA_RESPONSE);
|
|
|
|
translate(SSH1_CMSG_AUTH_PASSWORD);
|
|
|
|
translate(SSH1_CMSG_REQUEST_PTY);
|
|
|
|
translate(SSH1_CMSG_WINDOW_SIZE);
|
|
|
|
translate(SSH1_CMSG_EXEC_SHELL);
|
|
|
|
translate(SSH1_CMSG_EXEC_CMD);
|
|
|
|
translate(SSH1_SMSG_SUCCESS);
|
|
|
|
translate(SSH1_SMSG_FAILURE);
|
|
|
|
translate(SSH1_CMSG_STDIN_DATA);
|
|
|
|
translate(SSH1_SMSG_STDOUT_DATA);
|
|
|
|
translate(SSH1_SMSG_STDERR_DATA);
|
|
|
|
translate(SSH1_CMSG_EOF);
|
|
|
|
translate(SSH1_SMSG_EXIT_STATUS);
|
|
|
|
translate(SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);
|
|
|
|
translate(SSH1_MSG_CHANNEL_OPEN_FAILURE);
|
|
|
|
translate(SSH1_MSG_CHANNEL_DATA);
|
|
|
|
translate(SSH1_MSG_CHANNEL_CLOSE);
|
|
|
|
translate(SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION);
|
|
|
|
translate(SSH1_SMSG_X11_OPEN);
|
|
|
|
translate(SSH1_CMSG_PORT_FORWARD_REQUEST);
|
|
|
|
translate(SSH1_MSG_PORT_OPEN);
|
|
|
|
translate(SSH1_CMSG_AGENT_REQUEST_FORWARDING);
|
|
|
|
translate(SSH1_SMSG_AGENT_OPEN);
|
|
|
|
translate(SSH1_MSG_IGNORE);
|
|
|
|
translate(SSH1_CMSG_EXIT_CONFIRMATION);
|
|
|
|
translate(SSH1_CMSG_X11_REQUEST_FORWARDING);
|
|
|
|
translate(SSH1_CMSG_AUTH_RHOSTS_RSA);
|
|
|
|
translate(SSH1_MSG_DEBUG);
|
|
|
|
translate(SSH1_CMSG_REQUEST_COMPRESSION);
|
|
|
|
translate(SSH1_CMSG_AUTH_TIS);
|
|
|
|
translate(SSH1_SMSG_AUTH_TIS_CHALLENGE);
|
|
|
|
translate(SSH1_CMSG_AUTH_TIS_RESPONSE);
|
|
|
|
translate(SSH1_CMSG_AUTH_CCARD);
|
|
|
|
translate(SSH1_SMSG_AUTH_CCARD_CHALLENGE);
|
|
|
|
translate(SSH1_CMSG_AUTH_CCARD_RESPONSE);
|
|
|
|
return "unknown";
|
|
|
|
}
|
2018-06-09 08:07:18 +00:00
|
|
|
const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type)
|
2001-12-14 14:57:50 +00:00
|
|
|
{
|
2008-08-10 13:10:31 +00:00
|
|
|
translatea(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,SSH2_PKTCTX_GSSAPI);
|
|
|
|
translatea(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,SSH2_PKTCTX_GSSAPI);
|
|
|
|
translatea(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,SSH2_PKTCTX_GSSAPI);
|
|
|
|
translatea(SSH2_MSG_USERAUTH_GSSAPI_ERROR,SSH2_PKTCTX_GSSAPI);
|
|
|
|
translatea(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK,SSH2_PKTCTX_GSSAPI);
|
|
|
|
translatea(SSH2_MSG_USERAUTH_GSSAPI_MIC, SSH2_PKTCTX_GSSAPI);
|
2001-12-14 14:57:50 +00:00
|
|
|
translate(SSH2_MSG_DISCONNECT);
|
|
|
|
translate(SSH2_MSG_IGNORE);
|
|
|
|
translate(SSH2_MSG_UNIMPLEMENTED);
|
|
|
|
translate(SSH2_MSG_DEBUG);
|
|
|
|
translate(SSH2_MSG_SERVICE_REQUEST);
|
|
|
|
translate(SSH2_MSG_SERVICE_ACCEPT);
|
|
|
|
translate(SSH2_MSG_KEXINIT);
|
|
|
|
translate(SSH2_MSG_NEWKEYS);
|
2007-07-21 21:39:36 +00:00
|
|
|
translatek(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP);
|
|
|
|
translatek(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP);
|
2015-04-25 09:46:53 +00:00
|
|
|
translatek(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD, SSH2_PKTCTX_DHGEX);
|
2007-07-21 21:39:36 +00:00
|
|
|
translatek(SSH2_MSG_KEX_DH_GEX_REQUEST, SSH2_PKTCTX_DHGEX);
|
|
|
|
translatek(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX);
|
|
|
|
translatek(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX);
|
|
|
|
translatek(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX);
|
|
|
|
translatek(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX);
|
|
|
|
translatek(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX);
|
|
|
|
translatek(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX);
|
2014-11-01 09:45:20 +00:00
|
|
|
translatek(SSH2_MSG_KEX_ECDH_INIT, SSH2_PKTCTX_ECDHKEX);
|
|
|
|
translatek(SSH2_MSG_KEX_ECDH_REPLY, SSH2_PKTCTX_ECDHKEX);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
translatek(SSH2_MSG_KEXGSS_INIT, SSH2_PKTCTX_GSSKEX);
|
|
|
|
translatek(SSH2_MSG_KEXGSS_CONTINUE, SSH2_PKTCTX_GSSKEX);
|
|
|
|
translatek(SSH2_MSG_KEXGSS_COMPLETE, SSH2_PKTCTX_GSSKEX);
|
|
|
|
translatek(SSH2_MSG_KEXGSS_HOSTKEY, SSH2_PKTCTX_GSSKEX);
|
|
|
|
translatek(SSH2_MSG_KEXGSS_ERROR, SSH2_PKTCTX_GSSKEX);
|
|
|
|
translatek(SSH2_MSG_KEXGSS_GROUPREQ, SSH2_PKTCTX_GSSKEX);
|
|
|
|
translatek(SSH2_MSG_KEXGSS_GROUP, SSH2_PKTCTX_GSSKEX);
|
2001-12-14 14:57:50 +00:00
|
|
|
translate(SSH2_MSG_USERAUTH_REQUEST);
|
|
|
|
translate(SSH2_MSG_USERAUTH_FAILURE);
|
|
|
|
translate(SSH2_MSG_USERAUTH_SUCCESS);
|
|
|
|
translate(SSH2_MSG_USERAUTH_BANNER);
|
2007-07-21 21:39:36 +00:00
|
|
|
translatea(SSH2_MSG_USERAUTH_PK_OK, SSH2_PKTCTX_PUBLICKEY);
|
|
|
|
translatea(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, SSH2_PKTCTX_PASSWORD);
|
|
|
|
translatea(SSH2_MSG_USERAUTH_INFO_REQUEST, SSH2_PKTCTX_KBDINTER);
|
|
|
|
translatea(SSH2_MSG_USERAUTH_INFO_RESPONSE, SSH2_PKTCTX_KBDINTER);
|
2001-12-14 14:57:50 +00:00
|
|
|
translate(SSH2_MSG_GLOBAL_REQUEST);
|
|
|
|
translate(SSH2_MSG_REQUEST_SUCCESS);
|
|
|
|
translate(SSH2_MSG_REQUEST_FAILURE);
|
|
|
|
translate(SSH2_MSG_CHANNEL_OPEN);
|
|
|
|
translate(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
|
|
|
|
translate(SSH2_MSG_CHANNEL_OPEN_FAILURE);
|
|
|
|
translate(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
|
|
|
|
translate(SSH2_MSG_CHANNEL_DATA);
|
|
|
|
translate(SSH2_MSG_CHANNEL_EXTENDED_DATA);
|
|
|
|
translate(SSH2_MSG_CHANNEL_EOF);
|
|
|
|
translate(SSH2_MSG_CHANNEL_CLOSE);
|
|
|
|
translate(SSH2_MSG_CHANNEL_REQUEST);
|
|
|
|
translate(SSH2_MSG_CHANNEL_SUCCESS);
|
|
|
|
translate(SSH2_MSG_CHANNEL_FAILURE);
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
#undef translate
|
|
|
|
#undef translatec
|
2001-03-06 10:32:28 +00:00
|
|
|
|
2004-10-02 00:33:27 +00:00
|
|
|
/* Enumeration values for fields in SSH-1 packets */
|
|
|
|
enum {
|
|
|
|
PKT_END, PKT_INT, PKT_CHAR, PKT_DATA, PKT_STR, PKT_BIGNUM,
|
|
|
|
};
|
1999-11-08 17:36:08 +00:00
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_pkt_send(Ssh, struct PktOut *);
|
2018-05-18 06:22:58 +00:00
|
|
|
static void do_ssh1_login(void *vctx);
|
2018-05-18 06:22:59 +00:00
|
|
|
static void do_ssh2_userauth(void *vctx);
|
2018-05-31 17:42:47 +00:00
|
|
|
static void ssh2_connection_setup(Ssh ssh);
|
2018-05-18 06:22:59 +00:00
|
|
|
static void do_ssh2_connection(void *vctx);
|
2016-05-21 21:29:57 +00:00
|
|
|
static void ssh_channel_init(struct ssh_channel *c);
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static struct ssh_channel *ssh_channel_msg(Ssh ssh, PktIn *pktin);
|
2016-05-25 22:16:09 +00:00
|
|
|
static void ssh_channel_got_eof(struct ssh_channel *c);
|
2011-09-13 11:56:25 +00:00
|
|
|
static void ssh2_channel_check_close(struct ssh_channel *c);
|
2016-05-28 13:50:02 +00:00
|
|
|
static void ssh_channel_close_local(struct ssh_channel *c, char const *reason);
|
2011-09-13 11:44:03 +00:00
|
|
|
static void ssh_channel_destroy(struct ssh_channel *c);
|
2016-05-17 13:57:51 +00:00
|
|
|
static void ssh_channel_unthrottle(struct ssh_channel *c, int bufsize);
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_something_unimplemented(Ssh ssh, PktIn *pktin);
|
|
|
|
static void ssh2_general_packet_processing(Ssh ssh, PktIn *pktin);
|
2018-05-18 06:22:58 +00:00
|
|
|
static void ssh1_login_input(Ssh ssh);
|
2018-05-18 06:22:59 +00:00
|
|
|
static void ssh2_userauth_input(Ssh ssh);
|
|
|
|
static void ssh2_connection_input(Ssh ssh);
|
2002-01-10 19:50:53 +00:00
|
|
|
|
2016-03-25 15:56:31 +00:00
|
|
|
struct ssh_signkey_with_user_pref_id {
|
Invent a struct type for polymorphic SSH key data.
During last week's work, I made a mistake in which I got the arguments
backwards in one of the key-blob-generating functions - mistakenly
swapped the 'void *' key instance with the 'BinarySink *' output
destination - and I didn't spot the mistake until run time, because in
C you can implicitly convert both to and from void * and so there was
no compile-time failure of type checking.
Now that I've introduced the FROMFIELD macro that downcasts a pointer
to one field of a structure to retrieve a pointer to the whole
structure, I think I might start using that more widely to indicate
this kind of polymorphic subtyping. So now all the public-key
functions in the struct ssh_signkey vtable handle their data instance
in the form of a pointer to a subfield of a new zero-sized structure
type 'ssh_key', which outside the key implementations indicates 'this
is some kind of key instance but it could be of any type'; they
downcast that pointer internally using FROMFIELD in place of the
previous ordinary C cast, and return one by returning &foo->sshk for
whatever foo they've just made up.
The sshk member is not at the beginning of the structure, which means
all those FROMFIELDs and &key->sshk are actually adding and
subtracting an offset. Of course I could have put the member at the
start anyway, but I had the idea that it's actually a feature _not_ to
have the two types start at the same address, because it means you
should notice earlier rather than later if you absentmindedly cast
from one to the other directly rather than by the approved method (in
particular, if you accidentally assign one through a void * and back
without even _noticing_ you perpetrated a cast). In particular, this
enforces that you can't sfree() the thing even once without realising
you should instead of called the right freekey function. (I found
several bugs by this method during initial testing, so I think it's
already proved its worth!)
While I'm here, I've also renamed the vtable structure ssh_signkey to
ssh_keyalg, because it was a confusing name anyway - it describes the
_algorithm_ for handling all keys of that type, not a specific key. So
ssh_keyalg is the collection of code, and ssh_key is one instance of
the data it handles.
2018-05-27 07:32:21 +00:00
|
|
|
const ssh_keyalg *alg;
|
2016-03-25 15:56:31 +00:00
|
|
|
int id;
|
|
|
|
};
|
|
|
|
const static struct ssh_signkey_with_user_pref_id hostkey_algs[] = {
|
|
|
|
{ &ssh_ecdsa_ed25519, HK_ED25519 },
|
|
|
|
{ &ssh_ecdsa_nistp256, HK_ECDSA },
|
|
|
|
{ &ssh_ecdsa_nistp384, HK_ECDSA },
|
|
|
|
{ &ssh_ecdsa_nistp521, HK_ECDSA },
|
|
|
|
{ &ssh_dss, HK_DSA },
|
|
|
|
{ &ssh_rsa, HK_RSA },
|
2014-11-01 09:45:20 +00:00
|
|
|
};
|
2000-09-05 14:28:17 +00:00
|
|
|
|
2018-09-13 15:15:17 +00:00
|
|
|
const static struct ssh2_macalg *const macs[] = {
|
2013-02-20 23:30:55 +00:00
|
|
|
&ssh_hmac_sha256, &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5
|
2001-05-06 14:35:20 +00:00
|
|
|
};
|
2018-09-13 15:15:17 +00:00
|
|
|
const static struct ssh2_macalg *const buggymacs[] = {
|
2005-09-10 16:19:53 +00:00
|
|
|
&ssh_hmac_sha1_buggy, &ssh_hmac_sha1_96_buggy, &ssh_hmac_md5
|
2001-05-06 14:35:20 +00:00
|
|
|
};
|
2000-09-05 14:28:17 +00:00
|
|
|
|
2018-09-14 08:16:41 +00:00
|
|
|
static ssh_compressor *ssh_comp_none_init(void)
|
2002-10-25 13:26:33 +00:00
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-09-14 08:16:41 +00:00
|
|
|
static void ssh_comp_none_cleanup(ssh_compressor *handle)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
|
|
|
}
|
2018-09-14 08:16:41 +00:00
|
|
|
static ssh_decompressor *ssh_decomp_none_init(void)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
static void ssh_decomp_none_cleanup(ssh_decompressor *handle)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
static void ssh_comp_none_block(ssh_compressor *handle,
|
|
|
|
unsigned char *block, int len,
|
Move password-packet padding into the BPP module.
Now when we construct a packet containing sensitive data, we just set
a field saying '... and make it take up at least this much space, to
disguise its true size', and nothing in the rest of the system worries
about that flag until ssh2bpp.c acts on it.
Also, I've changed the strategy for doing the padding. Previously, we
were following the real packet with an SSH_MSG_IGNORE to make up the
size. But that was only a partial defence: it works OK against passive
traffic analysis, but an attacker proxying the TCP stream and
dribbling it out one byte at a time could still have found out the
size of the real packet by noting when the dribbled data provoked a
response. Now I put the SSH_MSG_IGNORE _first_, which should defeat
that attack.
But that in turn doesn't work when we're doing compression, because we
can't predict the compressed sizes accurately enough to make that
strategy sensible. Fortunately, compression provides an alternative
strategy anyway: if we've got zlib turned on when we send one of these
sensitive packets, then we can pad out the compressed zlib data as
much as we like by adding empty RFC1951 blocks (effectively chaining
ZLIB_PARTIAL_FLUSHes). So both strategies should now be dribble-proof.
2018-07-09 19:30:11 +00:00
|
|
|
unsigned char **outblock, int *outlen,
|
|
|
|
int minlen)
|
2018-07-10 20:27:43 +00:00
|
|
|
{
|
|
|
|
}
|
2018-09-14 08:16:41 +00:00
|
|
|
static int ssh_decomp_none_block(ssh_decompressor *handle,
|
|
|
|
unsigned char *block, int len,
|
2018-07-10 20:27:43 +00:00
|
|
|
unsigned char **outblock, int *outlen)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2018-09-14 08:16:41 +00:00
|
|
|
const static struct ssh_compression_alg ssh_comp_none = {
|
2011-03-04 22:34:47 +00:00
|
|
|
"none", NULL,
|
2002-10-25 13:26:33 +00:00
|
|
|
ssh_comp_none_init, ssh_comp_none_cleanup, ssh_comp_none_block,
|
2018-09-14 08:16:41 +00:00
|
|
|
ssh_decomp_none_init, ssh_decomp_none_cleanup, ssh_decomp_none_block,
|
Move password-packet padding into the BPP module.
Now when we construct a packet containing sensitive data, we just set
a field saying '... and make it take up at least this much space, to
disguise its true size', and nothing in the rest of the system worries
about that flag until ssh2bpp.c acts on it.
Also, I've changed the strategy for doing the padding. Previously, we
were following the real packet with an SSH_MSG_IGNORE to make up the
size. But that was only a partial defence: it works OK against passive
traffic analysis, but an attacker proxying the TCP stream and
dribbling it out one byte at a time could still have found out the
size of the real packet by noting when the dribbled data provoked a
response. Now I put the SSH_MSG_IGNORE _first_, which should defeat
that attack.
But that in turn doesn't work when we're doing compression, because we
can't predict the compressed sizes accurately enough to make that
strategy sensible. Fortunately, compression provides an alternative
strategy anyway: if we've got zlib turned on when we send one of these
sensitive packets, then we can pad out the compressed zlib data as
much as we like by adding empty RFC1951 blocks (effectively chaining
ZLIB_PARTIAL_FLUSHes). So both strategies should now be dribble-proof.
2018-07-09 19:30:11 +00:00
|
|
|
NULL
|
2000-09-05 14:28:17 +00:00
|
|
|
};
|
2018-09-14 08:16:41 +00:00
|
|
|
const static struct ssh_compression_alg *const compressions[] = {
|
2001-05-06 14:35:20 +00:00
|
|
|
&ssh_zlib, &ssh_comp_none
|
|
|
|
};
|
1999-01-08 13:02:13 +00:00
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
typedef void (*handler_fn_t)(Ssh ssh, PktIn *pktin);
|
|
|
|
typedef void (*chandler_fn_t)(Ssh ssh, PktIn *pktin, void *ctx);
|
|
|
|
typedef void (*cchandler_fn_t)(struct ssh_channel *, PktIn *, void *);
|
2012-08-25 11:12:14 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Each channel has a queue of outstanding CHANNEL_REQUESTS and their
|
|
|
|
* handlers.
|
|
|
|
*/
|
|
|
|
struct outstanding_channel_request {
|
|
|
|
cchandler_fn_t handler;
|
|
|
|
void *ctx;
|
|
|
|
struct outstanding_channel_request *next;
|
|
|
|
};
|
|
|
|
|
2000-09-15 10:48:42 +00:00
|
|
|
/*
|
|
|
|
* 2-3-4 tree storing channels.
|
|
|
|
*/
|
|
|
|
struct ssh_channel {
|
2002-10-25 11:30:33 +00:00
|
|
|
Ssh ssh; /* pointer back to main context */
|
2000-09-20 14:29:52 +00:00
|
|
|
unsigned remoteid, localid;
|
2000-09-15 10:48:42 +00:00
|
|
|
int type;
|
2005-01-22 16:06:21 +00:00
|
|
|
/* True if we opened this channel but server hasn't confirmed. */
|
|
|
|
int halfopen;
|
2002-09-15 13:24:00 +00:00
|
|
|
/*
|
2005-03-10 16:36:05 +00:00
|
|
|
* In SSH-1, this value contains four bits:
|
2002-09-15 13:24:00 +00:00
|
|
|
*
|
|
|
|
* 1 We have sent SSH1_MSG_CHANNEL_CLOSE.
|
|
|
|
* 2 We have sent SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION.
|
|
|
|
* 4 We have received SSH1_MSG_CHANNEL_CLOSE.
|
|
|
|
* 8 We have received SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION.
|
|
|
|
*
|
|
|
|
* A channel is completely finished with when all four bits are set.
|
2011-09-13 11:44:03 +00:00
|
|
|
*
|
|
|
|
* In SSH-2, the four bits mean:
|
|
|
|
*
|
|
|
|
* 1 We have sent SSH2_MSG_CHANNEL_EOF.
|
|
|
|
* 2 We have sent SSH2_MSG_CHANNEL_CLOSE.
|
|
|
|
* 4 We have received SSH2_MSG_CHANNEL_EOF.
|
|
|
|
* 8 We have received SSH2_MSG_CHANNEL_CLOSE.
|
|
|
|
*
|
|
|
|
* A channel is completely finished with when we have both sent
|
|
|
|
* and received CLOSE.
|
|
|
|
*
|
|
|
|
* The symbolic constants below use the SSH-2 terminology, which
|
|
|
|
* is a bit confusing in SSH-1, but we have to use _something_.
|
2002-09-15 13:24:00 +00:00
|
|
|
*/
|
2011-09-13 11:44:03 +00:00
|
|
|
#define CLOSES_SENT_EOF 1
|
|
|
|
#define CLOSES_SENT_CLOSE 2
|
|
|
|
#define CLOSES_RCVD_EOF 4
|
|
|
|
#define CLOSES_RCVD_CLOSE 8
|
2000-09-15 10:48:42 +00:00
|
|
|
int closes;
|
2010-07-04 22:53:53 +00:00
|
|
|
|
|
|
|
/*
|
2011-09-13 11:44:03 +00:00
|
|
|
* This flag indicates that an EOF is pending on the outgoing side
|
|
|
|
* of the channel: that is, wherever we're getting the data for
|
|
|
|
* this channel has sent us some data followed by EOF. We can't
|
|
|
|
* actually send the EOF until we've finished sending the data, so
|
|
|
|
* we set this flag instead to remind us to do so once our buffer
|
|
|
|
* is clear.
|
2010-07-04 22:53:53 +00:00
|
|
|
*/
|
2011-09-13 11:44:03 +00:00
|
|
|
int pending_eof;
|
2010-07-04 22:53:53 +00:00
|
|
|
|
2007-10-03 20:29:27 +00:00
|
|
|
/*
|
|
|
|
* True if this channel is causing the underlying connection to be
|
|
|
|
* throttled.
|
|
|
|
*/
|
|
|
|
int throttling_conn;
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* True if we currently have backed-up data on the direction of
|
|
|
|
* this channel pointing out of the SSH connection, and therefore
|
|
|
|
* would prefer the 'Channel' implementation not to read further
|
|
|
|
* local input if possible.
|
|
|
|
*/
|
|
|
|
int throttled_by_backlog;
|
|
|
|
|
2001-08-25 17:09:23 +00:00
|
|
|
union {
|
|
|
|
struct ssh2_data_channel {
|
|
|
|
bufchain outbuffer;
|
|
|
|
unsigned remwindow, remmaxpkt;
|
2007-08-04 14:32:06 +00:00
|
|
|
/* locwindow is signed so we can cope with excess data. */
|
2007-08-04 22:14:19 +00:00
|
|
|
int locwindow, locmaxwin;
|
2007-09-24 15:18:11 +00:00
|
|
|
/*
|
|
|
|
* remlocwin is the amount of local window that we think
|
|
|
|
* the remote end had available to it after it sent the
|
|
|
|
* last data packet or window adjust ack.
|
|
|
|
*/
|
|
|
|
int remlocwin;
|
|
|
|
/*
|
2012-08-25 11:12:14 +00:00
|
|
|
* These store the list of channel requests that haven't
|
2007-09-24 15:18:11 +00:00
|
|
|
* been acked.
|
|
|
|
*/
|
2012-08-25 11:12:14 +00:00
|
|
|
struct outstanding_channel_request *chanreq_head, *chanreq_tail;
|
2007-09-24 15:18:11 +00:00
|
|
|
enum { THROTTLED, UNTHROTTLING, UNTHROTTLED } throttle_state;
|
2001-08-25 17:09:23 +00:00
|
|
|
} v2;
|
|
|
|
} v;
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
|
2018-09-13 08:09:10 +00:00
|
|
|
ssh_sharing_connstate *sharectx; /* sharing context, if this is a
|
|
|
|
* downstream channel */
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
Channel *chan; /* handle the client side of this channel, if not */
|
2018-09-14 12:47:13 +00:00
|
|
|
SshChannel sc; /* entry point for chan to talk back to */
|
|
|
|
};
|
|
|
|
|
|
|
|
static int sshchannel_write(SshChannel *c, const void *buf, int len);
|
|
|
|
static void sshchannel_write_eof(SshChannel *c);
|
|
|
|
static void sshchannel_unclean_close(SshChannel *c, const char *err);
|
|
|
|
static void sshchannel_unthrottle(SshChannel *c, int bufsize);
|
|
|
|
static Conf *sshchannel_get_conf(SshChannel *c);
|
|
|
|
static void sshchannel_window_override_removed(SshChannel *c);
|
|
|
|
static void sshchannel_x11_sharing_handover(
|
|
|
|
SshChannel *c, ssh_sharing_connstate *share_cs, share_channel *share_chan,
|
|
|
|
const char *peer_addr, int peer_port, int endian,
|
|
|
|
int protomajor, int protominor, const void *initial_data, int initial_len);
|
|
|
|
|
|
|
|
const struct SshChannelVtable sshchannel_vtable = {
|
|
|
|
sshchannel_write,
|
|
|
|
sshchannel_write_eof,
|
|
|
|
sshchannel_unclean_close,
|
|
|
|
sshchannel_unthrottle,
|
|
|
|
sshchannel_get_conf,
|
|
|
|
sshchannel_window_override_removed,
|
|
|
|
sshchannel_x11_sharing_handover,
|
2000-09-15 10:48:42 +00:00
|
|
|
};
|
2000-09-29 08:43:47 +00:00
|
|
|
|
2001-08-08 20:44:35 +00:00
|
|
|
/*
|
2005-03-10 16:36:05 +00:00
|
|
|
* 2-3-4 tree storing remote->local port forwardings. SSH-1 and SSH-2
|
|
|
|
* use this structure in different ways, reflecting SSH-2's
|
2001-08-09 21:17:05 +00:00
|
|
|
* altogether saner approach to port forwarding.
|
|
|
|
*
|
2005-03-10 16:36:05 +00:00
|
|
|
* In SSH-1, you arrange a remote forwarding by sending the server
|
2001-08-09 21:17:05 +00:00
|
|
|
* the remote port number, and the local destination host:port.
|
|
|
|
* When a connection comes in, the server sends you back that
|
|
|
|
* host:port pair, and you connect to it. This is a ready-made
|
|
|
|
* security hole if you're not on the ball: a malicious server
|
|
|
|
* could send you back _any_ host:port pair, so if you trustingly
|
|
|
|
* connect to the address it gives you then you've just opened the
|
|
|
|
* entire inside of your corporate network just by connecting
|
|
|
|
* through it to a dodgy SSH server. Hence, we must store a list of
|
|
|
|
* host:port pairs we _are_ trying to forward to, and reject a
|
|
|
|
* connection request from the server if it's not in the list.
|
|
|
|
*
|
2005-03-10 16:36:05 +00:00
|
|
|
* In SSH-2, each side of the connection minds its own business and
|
2001-08-09 21:17:05 +00:00
|
|
|
* doesn't send unnecessary information to the other. You arrange a
|
|
|
|
* remote forwarding by sending the server just the remote port
|
|
|
|
* number. When a connection comes in, the server tells you which
|
|
|
|
* of its ports was connected to; and _you_ have to remember what
|
|
|
|
* local host:port pair went with that port number.
|
|
|
|
*
|
2005-03-10 16:36:05 +00:00
|
|
|
* Hence, in SSH-1 this structure is indexed by destination
|
|
|
|
* host:port pair, whereas in SSH-2 it is indexed by source port.
|
2001-08-08 20:44:35 +00:00
|
|
|
*/
|
2004-12-28 14:07:05 +00:00
|
|
|
struct ssh_portfwd; /* forward declaration */
|
|
|
|
|
2001-08-08 20:44:35 +00:00
|
|
|
struct ssh_rportfwd {
|
2001-08-09 21:17:05 +00:00
|
|
|
unsigned sport, dport;
|
2013-11-17 14:03:21 +00:00
|
|
|
char *shost, *dhost;
|
2018-09-14 16:04:39 +00:00
|
|
|
int addressfamily;
|
|
|
|
char *log_description; /* name of remote listening port, for logging */
|
2018-09-13 08:09:10 +00:00
|
|
|
ssh_sharing_connstate *share_ctx;
|
2018-09-14 16:04:39 +00:00
|
|
|
PortFwdRecord *pfr;
|
2001-08-08 20:44:35 +00:00
|
|
|
};
|
2013-11-17 14:03:21 +00:00
|
|
|
|
|
|
|
static void free_rportfwd(struct ssh_rportfwd *pf)
|
|
|
|
{
|
|
|
|
if (pf) {
|
2018-09-14 16:04:39 +00:00
|
|
|
sfree(pf->log_description);
|
2013-11-17 14:03:21 +00:00
|
|
|
sfree(pf->shost);
|
|
|
|
sfree(pf->dhost);
|
|
|
|
sfree(pf);
|
|
|
|
}
|
|
|
|
}
|
2004-12-28 14:07:05 +00:00
|
|
|
|
2004-11-24 20:35:15 +00:00
|
|
|
static void ssh1_protocol_setup(Ssh ssh);
|
|
|
|
static void ssh2_protocol_setup(Ssh ssh);
|
2013-11-17 14:05:41 +00:00
|
|
|
static void ssh2_bare_connection_protocol_setup(Ssh ssh);
|
2018-09-11 15:23:38 +00:00
|
|
|
static void ssh_size(Backend *be, int width, int height);
|
|
|
|
static void ssh_special(Backend *be, Telnet_Special);
|
2001-08-25 17:09:23 +00:00
|
|
|
static int ssh2_try_send(struct ssh_channel *c);
|
2016-04-23 14:51:45 +00:00
|
|
|
static int ssh_send_channel_data(struct ssh_channel *c,
|
|
|
|
const char *buf, int len);
|
2002-10-25 11:30:33 +00:00
|
|
|
static void ssh_throttle_all(Ssh ssh, int enable, int bufsize);
|
2007-08-06 20:56:52 +00:00
|
|
|
static void ssh2_set_window(struct ssh_channel *c, int newwin);
|
2018-09-11 15:23:38 +00:00
|
|
|
static int ssh_sendbuffer(Backend *be);
|
2005-01-11 19:33:41 +00:00
|
|
|
static int ssh_do_close(Ssh ssh, int notify_exit);
|
2012-09-18 21:42:48 +00:00
|
|
|
static void ssh2_timer(void *ctx, unsigned long now);
|
2018-04-14 13:48:02 +00:00
|
|
|
static int ssh2_timer_update(Ssh ssh, unsigned long rekey_time);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
#ifndef NO_GSSAPI
|
2018-05-01 17:54:04 +00:00
|
|
|
static void ssh2_gss_update(Ssh ssh, int definitely_rekeying);
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static PktOut *ssh2_gss_authpacket(Ssh ssh, Ssh_gss_ctx gss_ctx,
|
|
|
|
const char *authtype);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
#endif
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_unexpected(Ssh ssh, PktIn *pktin);
|
2018-05-18 06:22:56 +00:00
|
|
|
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
struct queued_handler;
|
|
|
|
struct queued_handler {
|
|
|
|
int msg1, msg2;
|
|
|
|
chandler_fn_t handler;
|
|
|
|
void *ctx;
|
|
|
|
struct queued_handler *next;
|
|
|
|
};
|
2004-11-24 20:35:15 +00:00
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
/*
|
|
|
|
* Enumeration of high-level classes of reason why we might need to do
|
|
|
|
* a repeat key exchange. The full detailed reason in human-readable
|
|
|
|
* form for the Event Log is kept in ssh->rekey_reason, but
|
|
|
|
* ssh->rekey_class is a variable with this enum type which is used to
|
|
|
|
* discriminate between classes of reason that the code needs to treat
|
|
|
|
* differently.
|
|
|
|
*
|
2018-05-18 12:46:36 +00:00
|
|
|
* RK_NONE == 0 is the value indicating that no rekey is currently
|
|
|
|
* needed at all. RK_INITIAL indicates that we haven't even done the
|
|
|
|
* _first_ key exchange yet. RK_NORMAL is the usual case.
|
2018-05-18 06:22:58 +00:00
|
|
|
* RK_GSS_UPDATE indicates that we're rekeying because we've just got
|
|
|
|
* new GSSAPI credentials (hence there's no point in doing a
|
|
|
|
* preliminary check for new GSS creds, because we already know the
|
|
|
|
* answer); RK_POST_USERAUTH indicates that _if_ we're going to need a
|
|
|
|
* post-userauth immediate rekey for any reason, this is the moment to
|
|
|
|
* do it.
|
|
|
|
*
|
|
|
|
* So RK_POST_USERAUTH only tells the transport layer to _consider_
|
|
|
|
* rekeying, not to definitely do it. Also, that one enum value is
|
|
|
|
* special in that do_ssh2_transport fills in the reason text after it
|
|
|
|
* decides whether it needs a rekey at all. In the other cases,
|
|
|
|
* rekey_reason is set up at the same time as rekey_class.
|
|
|
|
*/
|
2018-05-18 12:46:36 +00:00
|
|
|
enum RekeyClass {
|
|
|
|
RK_NONE = 0,
|
|
|
|
RK_INITIAL,
|
|
|
|
RK_NORMAL,
|
|
|
|
RK_POST_USERAUTH,
|
|
|
|
RK_GSS_UPDATE
|
|
|
|
};
|
2018-05-18 06:22:58 +00:00
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
struct ssh_tag {
|
2005-08-30 20:38:57 +00:00
|
|
|
char *v_c, *v_s;
|
2018-09-19 16:37:00 +00:00
|
|
|
struct ssh_version_receiver version_receiver;
|
2018-09-13 15:41:46 +00:00
|
|
|
ssh_hash *exhash;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
|
|
|
Socket s;
|
|
|
|
|
2018-05-27 08:29:33 +00:00
|
|
|
const Plug_vtable *plugvt;
|
2018-09-11 15:23:38 +00:00
|
|
|
Backend backend;
|
2018-05-27 08:29:33 +00:00
|
|
|
|
2018-09-11 14:02:59 +00:00
|
|
|
Ldisc *ldisc;
|
2018-09-11 14:17:16 +00:00
|
|
|
LogContext *logctx;
|
2002-10-26 10:16:19 +00:00
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
unsigned char session_key[32];
|
|
|
|
int v1_remote_protoflags;
|
|
|
|
int v1_local_protoflags;
|
|
|
|
int agentfwd_enabled;
|
|
|
|
int X11_fwd_enabled;
|
|
|
|
int remote_bugs;
|
|
|
|
const struct ssh_kex *kex;
|
2018-06-03 11:58:05 +00:00
|
|
|
const ssh_keyalg *hostkey_alg;
|
2013-08-18 06:48:20 +00:00
|
|
|
char *hostkey_str; /* string representation, for easy checking in rekeys */
|
2006-03-12 19:24:05 +00:00
|
|
|
unsigned char v2_session_id[SSH2_KEX_MAX_HASH_LEN];
|
2005-08-31 20:43:06 +00:00
|
|
|
int v2_session_id_len;
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
int v2_cbc_ignore_workaround;
|
|
|
|
int v2_out_cipherblksize;
|
2018-09-14 07:48:54 +00:00
|
|
|
struct dh_ctx *dh_ctx;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
2013-11-17 14:05:41 +00:00
|
|
|
int bare_connection;
|
|
|
|
int attempting_connshare;
|
2018-09-13 08:09:10 +00:00
|
|
|
ssh_sharing_state *connshare;
|
2013-11-17 14:05:41 +00:00
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
char *savedhost;
|
|
|
|
int savedport;
|
|
|
|
int send_ok;
|
|
|
|
int echoing, editing;
|
|
|
|
|
2015-11-22 14:33:28 +00:00
|
|
|
int session_started;
|
2018-09-12 08:10:51 +00:00
|
|
|
Frontend *frontend;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
2004-06-03 10:36:27 +00:00
|
|
|
int ospeed, ispeed; /* temporaries */
|
2002-10-25 11:30:33 +00:00
|
|
|
int term_width, term_height;
|
|
|
|
|
|
|
|
tree234 *channels; /* indexed by local id */
|
|
|
|
struct ssh_channel *mainchan; /* primary session channel */
|
2006-08-28 15:12:37 +00:00
|
|
|
int ncmode; /* is primary channel direct-tcpip? */
|
2002-10-25 11:30:33 +00:00
|
|
|
int exitcode;
|
2005-01-11 19:33:41 +00:00
|
|
|
int close_expected;
|
2005-05-21 16:49:27 +00:00
|
|
|
int clean_exit;
|
2018-06-03 05:46:28 +00:00
|
|
|
int disconnect_message_seen;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
2018-09-14 16:04:39 +00:00
|
|
|
tree234 *rportfwds;
|
|
|
|
PortFwdManager *portfwdmgr;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
2018-09-17 11:14:00 +00:00
|
|
|
ConnectionLayer cl;
|
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
enum {
|
|
|
|
SSH_STATE_PREPACKET,
|
|
|
|
SSH_STATE_BEFORE_SIZE,
|
|
|
|
SSH_STATE_INTERMED,
|
|
|
|
SSH_STATE_SESSION,
|
|
|
|
SSH_STATE_CLOSED
|
|
|
|
} state;
|
|
|
|
|
|
|
|
int size_needed, eof_needed;
|
2011-09-13 11:44:03 +00:00
|
|
|
int sent_console_eof;
|
2011-09-13 15:38:12 +00:00
|
|
|
int got_pty; /* affects EOF behaviour on main channel */
|
2002-10-25 11:30:33 +00:00
|
|
|
|
2018-09-19 20:56:11 +00:00
|
|
|
PktOutQueue outq;
|
2004-11-24 19:23:02 +00:00
|
|
|
int queueing;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Gross hack: pscp will try to start SFTP but fall back to
|
|
|
|
* scp1 if that fails. This variable is the means by which
|
|
|
|
* scp.c can reach into the SSH code and find out which one it
|
|
|
|
* got.
|
|
|
|
*/
|
|
|
|
int fallback_cmd;
|
|
|
|
|
2018-05-18 06:22:59 +00:00
|
|
|
bufchain banner; /* accumulates banners during do_ssh2_userauth */
|
2002-10-25 11:30:33 +00:00
|
|
|
|
2008-11-17 18:38:09 +00:00
|
|
|
struct X11Display *x11disp;
|
2013-11-17 14:05:10 +00:00
|
|
|
struct X11FakeAuth *x11auth;
|
|
|
|
tree234 *x11authtree;
|
2002-10-26 11:23:15 +00:00
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
int version;
|
2007-10-03 20:29:27 +00:00
|
|
|
int conn_throttle_count;
|
2002-10-25 11:30:33 +00:00
|
|
|
int overall_bufsize;
|
|
|
|
int throttled_all;
|
|
|
|
int v1_stdout_throttling;
|
|
|
|
|
2004-11-24 20:35:15 +00:00
|
|
|
int do_ssh1_connection_crstate;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
|
|
|
void *do_ssh_init_state;
|
|
|
|
void *do_ssh1_login_state;
|
|
|
|
void *do_ssh2_transport_state;
|
2018-05-18 06:22:59 +00:00
|
|
|
void *do_ssh2_userauth_state;
|
|
|
|
void *do_ssh2_connection_state;
|
2013-11-17 14:05:41 +00:00
|
|
|
void *do_ssh_connection_init_state;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
bufchain incoming_data;
|
|
|
|
struct IdempotentCallback incoming_data_consumer;
|
|
|
|
int incoming_data_seen_eof;
|
|
|
|
char *incoming_data_eof_message;
|
|
|
|
|
2018-09-19 20:56:11 +00:00
|
|
|
PktInQueue pq_full;
|
Make the rdpkt functions output to a PacketQueue.
Each of the coroutines that parses the incoming wire data into a
stream of 'struct Packet' now delivers those packets to a PacketQueue
called ssh->pq_full (containing the full, unfiltered stream of all
packets received on the SSH connection), replacing the old API in
which each coroutine would directly return a 'struct Packet *' to its
caller, or NULL if it didn't have one ready yet.
This simplifies the function-call API of the rdpkt coroutines (they
now return void). It increases the complexity at the other end,
because we've now got a function ssh_process_pq_full (scheduled as an
idempotent callback whenever rdpkt appends anything to the queue)
which pulls things out of the queue and passes them to ssh->protocol.
But that's only a temporary complexity increase; by the time I finish
the upcoming stream of refactorings, there won't be two chained
functions there any more.
One small workaround I had to add in this commit is a flag called
'pending_newkeys', which ssh2_rdpkt sets when it's just returned an
SSH_MSG_NEWKEYS packet, and then waits for the transport layer to
process the NEWKEYS and set up the new encryption context before
processing any more wire data. This wasn't necessary before, because
the old architecture was naturally synchronous - ssh2_rdpkt would
return a NEWKEYS, which would be immediately passed to
do_ssh2_transport, which would finish processing it immediately, and
by the time ssh2_rdpkt was next called, the keys would already be in
place.
This change adds a big while loop around the whole of each rdpkt
function, so it's easiest to read it as a whitespace-ignored diff.
2018-05-18 06:22:57 +00:00
|
|
|
struct IdempotentCallback pq_full_consumer;
|
|
|
|
|
2018-09-19 20:56:11 +00:00
|
|
|
PktInQueue pq_ssh1_login;
|
2018-05-18 06:22:58 +00:00
|
|
|
struct IdempotentCallback ssh1_login_icb;
|
|
|
|
|
2018-09-19 20:56:11 +00:00
|
|
|
PktInQueue pq_ssh1_connection;
|
2018-05-18 06:22:58 +00:00
|
|
|
struct IdempotentCallback ssh1_connection_icb;
|
|
|
|
|
2018-09-19 20:56:11 +00:00
|
|
|
PktInQueue pq_ssh2_transport;
|
2018-05-18 06:22:58 +00:00
|
|
|
struct IdempotentCallback ssh2_transport_icb;
|
|
|
|
|
2018-09-19 20:56:11 +00:00
|
|
|
PktInQueue pq_ssh2_userauth;
|
2018-05-18 06:22:59 +00:00
|
|
|
struct IdempotentCallback ssh2_userauth_icb;
|
|
|
|
|
2018-09-19 20:56:11 +00:00
|
|
|
PktInQueue pq_ssh2_connection;
|
2018-05-18 06:22:59 +00:00
|
|
|
struct IdempotentCallback ssh2_connection_icb;
|
2018-05-18 06:22:58 +00:00
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
bufchain user_input;
|
|
|
|
struct IdempotentCallback user_input_consumer;
|
|
|
|
|
2018-06-09 08:09:10 +00:00
|
|
|
bufchain outgoing_data;
|
|
|
|
struct IdempotentCallback outgoing_data_sender;
|
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
const char *rekey_reason;
|
|
|
|
enum RekeyClass rekey_class;
|
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
PacketLogSettings pls;
|
|
|
|
BinaryPacketProtocol *bpp;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
void (*general_packet_processing)(Ssh ssh, PktIn *pkt);
|
2018-05-18 06:22:58 +00:00
|
|
|
void (*current_user_input_fn) (Ssh ssh);
|
2003-01-12 14:48:29 +00:00
|
|
|
|
|
|
|
/*
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
* We maintain our own copy of a Conf structure here. That way,
|
|
|
|
* when we're passed a new one for reconfiguration, we can check
|
|
|
|
* the differences and potentially reconfigure port forwardings
|
|
|
|
* etc in mid-session.
|
2003-01-12 14:48:29 +00:00
|
|
|
*/
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
Conf *conf;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Dynamically allocated username string created during SSH
|
|
|
|
* login. Stored in here rather than in the coroutine state so
|
|
|
|
* that it'll be reliably freed if we shut down the SSH session
|
|
|
|
* at some unexpected moment.
|
|
|
|
*/
|
|
|
|
char *username;
|
2003-04-28 11:41:39 +00:00
|
|
|
|
|
|
|
/*
|
2005-02-17 18:34:24 +00:00
|
|
|
* Used to transfer data back from async callbacks.
|
2003-04-28 11:41:39 +00:00
|
|
|
*/
|
|
|
|
void *agent_response;
|
|
|
|
int agent_response_len;
|
2005-02-17 18:34:24 +00:00
|
|
|
int user_response;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The SSH connection can be set as `frozen', meaning we are
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
* not currently accepting incoming data from the network.
|
2005-02-17 18:34:24 +00:00
|
|
|
*/
|
|
|
|
int frozen;
|
2004-11-24 20:35:15 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Dispatch table for packet types that we may have to deal
|
|
|
|
* with at any time.
|
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
handler_fn_t packet_dispatch[SSH_MAX_MSG];
|
2004-11-27 13:20:21 +00:00
|
|
|
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
/*
|
|
|
|
* Queues of one-off handler functions for success/failure
|
|
|
|
* indications from a request.
|
|
|
|
*/
|
|
|
|
struct queued_handler *qhead, *qtail;
|
2012-08-21 22:04:56 +00:00
|
|
|
handler_fn_t q_saved_handler1, q_saved_handler2;
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
|
2004-11-27 13:20:21 +00:00
|
|
|
/*
|
|
|
|
* This module deals with sending keepalives.
|
|
|
|
*/
|
|
|
|
Pinger pinger;
|
2004-11-27 14:29:20 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Track incoming and outgoing data sizes and time, for
|
|
|
|
* size-based rekeys.
|
|
|
|
*/
|
2018-06-09 08:09:10 +00:00
|
|
|
unsigned long incoming_data_size, outgoing_data_size;
|
2004-12-24 13:39:32 +00:00
|
|
|
unsigned long max_data_size;
|
2004-11-27 14:29:20 +00:00
|
|
|
int kex_in_progress;
|
2012-09-18 21:42:48 +00:00
|
|
|
unsigned long next_rekey, last_rekey;
|
2015-05-15 10:15:42 +00:00
|
|
|
const char *deferred_rekey_reason;
|
2008-08-10 13:10:31 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Fully qualified host name, which we need if doing GSSAPI.
|
|
|
|
*/
|
|
|
|
char *fullhostname;
|
2010-09-25 07:16:56 +00:00
|
|
|
|
|
|
|
#ifndef NO_GSSAPI
|
|
|
|
/*
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
* GSSAPI libraries for this session. We need them at key exchange
|
|
|
|
* and userauth time.
|
|
|
|
*
|
|
|
|
* And the gss_ctx we setup at initial key exchange will be used
|
|
|
|
* during gssapi-keyex userauth time as well.
|
2010-09-25 07:16:56 +00:00
|
|
|
*/
|
|
|
|
struct ssh_gss_liblist *gsslibs;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
struct ssh_gss_library *gsslib;
|
|
|
|
int gss_status;
|
|
|
|
time_t gss_cred_expiry; /* Re-delegate if newer */
|
|
|
|
unsigned long gss_ctxt_lifetime; /* Re-delegate when short */
|
|
|
|
Ssh_gss_name gss_srv_name; /* Cached for KEXGSS */
|
|
|
|
Ssh_gss_ctx gss_ctx; /* Saved for gssapi-keyex */
|
|
|
|
tree234 *transient_hostkey_cache;
|
2010-09-25 07:16:56 +00:00
|
|
|
#endif
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
int gss_kex_used; /* outside ifdef; always FALSE if NO_GSSAPI */
|
2016-03-21 06:50:50 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The last list returned from get_specials.
|
|
|
|
*/
|
|
|
|
struct telnet_special *specials;
|
Add manual cross-certification of new host keys.
If a server offers host key algorithms that we don't have a stored key
for, they will now appear in a submenu of the Special Commands menu.
Selecting one will force a repeat key exchange with that key, and if
it succeeds, will add the new host key to the cache. The idea is that
the new key sent by the server is protected by the crypto established
in the previous key exchange, so this is just as safe as typing some
command like 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub' at the
server prompt and transcribing the results manually.
This allows switching over to newer host key algorithms if the client
has begun to support them (e.g. people using PuTTY's new ECC
functionality for the first time), or if the server has acquired a new
key (e.g. due to a server OS upgrade).
At the moment, it's only available manually, for a single host key
type at a time. Automating it is potentially controversial for
security policy reasons (what if someone doesn't agree this is what
they want in their host key cache, or doesn't want to switch over to
using whichever of the keys PuTTY would now put top of the list?), for
code plumbing reasons (chaining several of these rekeys might be more
annoying than doing one at a time) and for CPU usage reasons (rekeys
are expensive), but even so, it might turn out to be a good idea in
future.
2016-03-21 07:25:31 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* List of host key algorithms for which we _don't_ have a stored
|
|
|
|
* host key. These are indices into the main hostkey_algs[] array
|
|
|
|
*/
|
|
|
|
int uncert_hostkeys[lenof(hostkey_algs)];
|
|
|
|
int n_uncert_hostkeys;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Flag indicating that the current rekey is intended to finish
|
|
|
|
* with a newly cross-certified host key.
|
|
|
|
*/
|
|
|
|
int cross_certifying;
|
2017-01-29 20:24:15 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Any asynchronous query to our SSH agent that we might have in
|
|
|
|
* flight from the main authentication loop. (Queries from
|
|
|
|
* agent-forwarding channels live in their channel structure.)
|
|
|
|
*/
|
|
|
|
agent_pending_query *auth_agent_query;
|
2017-11-27 19:29:47 +00:00
|
|
|
|
|
|
|
int need_random_unref;
|
2002-10-25 11:30:33 +00:00
|
|
|
};
|
2000-09-29 15:56:33 +00:00
|
|
|
|
2016-05-22 21:14:00 +00:00
|
|
|
static const char *ssh_pkt_type(Ssh ssh, int type)
|
|
|
|
{
|
|
|
|
if (ssh->version == 1)
|
|
|
|
return ssh1_pkt_type(type);
|
|
|
|
else
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
return ssh2_pkt_type(ssh->pls.kctx, ssh->pls.actx, type);
|
2016-05-22 21:14:00 +00:00
|
|
|
}
|
|
|
|
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
Frontend *ssh_get_frontend(Ssh ssh)
|
|
|
|
{
|
|
|
|
return ssh->frontend;
|
|
|
|
}
|
|
|
|
|
2002-12-19 14:24:28 +00:00
|
|
|
#define logevent(s) logevent(ssh->frontend, s)
|
2002-10-26 12:58:13 +00:00
|
|
|
|
|
|
|
/* logevent, only printf-formatted. */
|
2003-05-04 14:18:18 +00:00
|
|
|
static void logeventf(Ssh ssh, const char *fmt, ...)
|
2002-10-26 12:58:13 +00:00
|
|
|
{
|
|
|
|
va_list ap;
|
2002-11-07 19:49:03 +00:00
|
|
|
char *buf;
|
2002-10-26 12:58:13 +00:00
|
|
|
|
|
|
|
va_start(ap, fmt);
|
2002-11-07 19:49:03 +00:00
|
|
|
buf = dupvprintf(fmt, ap);
|
2002-10-26 12:58:13 +00:00
|
|
|
va_end(ap);
|
2002-11-07 19:49:03 +00:00
|
|
|
logevent(buf);
|
|
|
|
sfree(buf);
|
2002-10-26 12:58:13 +00:00
|
|
|
}
|
|
|
|
|
2012-08-27 21:55:45 +00:00
|
|
|
static void bomb_out(Ssh ssh, char *text)
|
|
|
|
{
|
|
|
|
ssh_do_close(ssh, FALSE);
|
|
|
|
logevent(text);
|
|
|
|
connection_fatal(ssh->frontend, "%s", text);
|
|
|
|
sfree(text);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define bombout(msg) bomb_out(ssh, dupprintf msg)
|
2002-10-26 12:58:13 +00:00
|
|
|
|
2001-05-06 14:35:20 +00:00
|
|
|
static int ssh_channelcmp(void *av, void *bv)
|
|
|
|
{
|
|
|
|
struct ssh_channel *a = (struct ssh_channel *) av;
|
|
|
|
struct ssh_channel *b = (struct ssh_channel *) bv;
|
|
|
|
if (a->localid < b->localid)
|
|
|
|
return -1;
|
|
|
|
if (a->localid > b->localid)
|
|
|
|
return +1;
|
2000-09-15 10:48:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2001-05-06 14:35:20 +00:00
|
|
|
static int ssh_channelfind(void *av, void *bv)
|
|
|
|
{
|
|
|
|
unsigned *a = (unsigned *) av;
|
|
|
|
struct ssh_channel *b = (struct ssh_channel *) bv;
|
|
|
|
if (*a < b->localid)
|
|
|
|
return -1;
|
|
|
|
if (*a > b->localid)
|
|
|
|
return +1;
|
2000-09-15 10:48:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-08-09 21:17:05 +00:00
|
|
|
static int ssh_rportcmp_ssh1(void *av, void *bv)
|
2001-08-08 20:44:35 +00:00
|
|
|
{
|
|
|
|
struct ssh_rportfwd *a = (struct ssh_rportfwd *) av;
|
|
|
|
struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv;
|
|
|
|
int i;
|
2001-08-09 21:17:05 +00:00
|
|
|
if ( (i = strcmp(a->dhost, b->dhost)) != 0)
|
2001-08-08 20:44:35 +00:00
|
|
|
return i < 0 ? -1 : +1;
|
2001-08-09 21:17:05 +00:00
|
|
|
if (a->dport > b->dport)
|
|
|
|
return +1;
|
|
|
|
if (a->dport < b->dport)
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ssh_rportcmp_ssh2(void *av, void *bv)
|
|
|
|
{
|
|
|
|
struct ssh_rportfwd *a = (struct ssh_rportfwd *) av;
|
|
|
|
struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv;
|
2013-11-17 14:03:21 +00:00
|
|
|
int i;
|
|
|
|
if ( (i = strcmp(a->shost, b->shost)) != 0)
|
|
|
|
return i < 0 ? -1 : +1;
|
2001-08-09 21:17:05 +00:00
|
|
|
if (a->sport > b->sport)
|
2001-08-08 20:44:35 +00:00
|
|
|
return +1;
|
2001-08-09 21:17:05 +00:00
|
|
|
if (a->sport < b->sport)
|
|
|
|
return -1;
|
2001-08-08 20:44:35 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-09-19 12:20:03 +00:00
|
|
|
static unsigned alloc_channel_id(Ssh ssh)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2001-04-17 08:24:29 +00:00
|
|
|
const unsigned CHANNEL_NUMBER_OFFSET = 256;
|
2018-09-19 12:20:03 +00:00
|
|
|
search234_state ss;
|
2001-04-16 17:18:24 +00:00
|
|
|
|
|
|
|
/*
|
2018-09-19 12:20:03 +00:00
|
|
|
* First-fit allocation of channel numbers: we always pick the
|
|
|
|
* lowest unused one.
|
|
|
|
*
|
|
|
|
* Every channel before that, and no channel after it, has an ID
|
|
|
|
* exactly equal to its tree index plus CHANNEL_NUMBER_OFFSET. So
|
|
|
|
* we can use the search234 system to identify the length of that
|
|
|
|
* initial sequence, in a single log-time pass down the channels
|
|
|
|
* tree.
|
2001-04-16 17:18:24 +00:00
|
|
|
*/
|
2018-09-19 12:20:03 +00:00
|
|
|
search234_start(&ss, ssh->channels);
|
|
|
|
while (ss.element) {
|
|
|
|
struct ssh_channel *c = (struct ssh_channel *)ss.element;
|
|
|
|
if (c->localid == ss.index + CHANNEL_NUMBER_OFFSET)
|
|
|
|
search234_step(&ss, +1);
|
|
|
|
else
|
|
|
|
search234_step(&ss, -1);
|
2001-04-16 17:18:24 +00:00
|
|
|
}
|
2018-09-19 12:20:03 +00:00
|
|
|
|
2001-04-16 17:18:24 +00:00
|
|
|
/*
|
2018-09-19 12:20:03 +00:00
|
|
|
* Now ss.index gives exactly the number of channels in that
|
|
|
|
* initial sequence. So adding CHANNEL_NUMBER_OFFSET to it must
|
|
|
|
* give precisely the lowest unused channel number.
|
2001-04-16 17:18:24 +00:00
|
|
|
*/
|
2018-09-19 12:20:03 +00:00
|
|
|
return ss.index + CHANNEL_NUMBER_OFFSET;
|
2001-04-16 17:18:24 +00:00
|
|
|
}
|
|
|
|
|
2018-05-26 07:31:34 +00:00
|
|
|
static void c_write_stderr(int trusted, const void *vbuf, int len)
|
2005-10-30 20:24:09 +00:00
|
|
|
{
|
2018-05-26 07:31:34 +00:00
|
|
|
const char *buf = (const char *)vbuf;
|
2005-10-30 20:24:09 +00:00
|
|
|
int i;
|
|
|
|
for (i = 0; i < len; i++)
|
2006-09-05 21:41:38 +00:00
|
|
|
if (buf[i] != '\r' && (trusted || buf[i] == '\n' || (buf[i] & 0x60)))
|
2005-10-30 20:24:09 +00:00
|
|
|
fputc(buf[i], stderr);
|
|
|
|
}
|
|
|
|
|
2018-05-26 07:31:34 +00:00
|
|
|
static void c_write(Ssh ssh, const void *buf, int len)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2005-10-30 20:24:09 +00:00
|
|
|
if (flags & FLAG_STDERR)
|
|
|
|
c_write_stderr(1, buf, len);
|
|
|
|
else
|
|
|
|
from_backend(ssh->frontend, 1, buf, len);
|
2000-09-27 09:36:39 +00:00
|
|
|
}
|
|
|
|
|
2003-02-01 17:24:27 +00:00
|
|
|
static void c_write_str(Ssh ssh, const char *buf)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2002-10-25 11:30:33 +00:00
|
|
|
c_write(ssh, buf, strlen(buf));
|
2001-03-03 13:53:44 +00:00
|
|
|
}
|
|
|
|
|
2018-06-06 06:17:32 +00:00
|
|
|
static int s_write(Ssh ssh, const void *data, int len)
|
New logging mode, which records the exact bytes sent over the wire
in an SSH connection _in addition_ to the decrypted packets. This
will hopefully come in useful for debugging wire data corruption
issues: you can strace the server, enable this mode in the client,
and compare the sent and received data.
I'd _like_ to have this mode also log Diffie-Hellman private
exponents, session IDs, encryption and MAC keys, so that the
resulting log file could be used to independently verify the
correctness of all cryptographic operations performed by PuTTY.
However, I haven't been able to convince myself that the security
implications are acceptable. (It doesn't matter that this
information would permit an attacker to decrypt the session, because
the _already_ decrypted session is stored alongside it in the log
file. And I'm not planning, under any circumstances, to log users'
private keys. But gaining access to the log file while the session
was still running would permit an attacker to _hijack_ the session,
and that's the iffy bit.)
[originally from svn r6835]
2006-08-29 19:07:11 +00:00
|
|
|
{
|
2018-02-07 19:56:28 +00:00
|
|
|
if (len && ssh->logctx)
|
2008-11-11 07:47:27 +00:00
|
|
|
log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len,
|
2013-11-17 14:05:41 +00:00
|
|
|
0, NULL, NULL, 0, NULL);
|
2013-11-17 14:04:56 +00:00
|
|
|
if (!ssh->s)
|
|
|
|
return 0;
|
2018-05-26 07:31:34 +00:00
|
|
|
return sk_write(ssh->s, data, len);
|
New logging mode, which records the exact bytes sent over the wire
in an SSH connection _in addition_ to the decrypted packets. This
will hopefully come in useful for debugging wire data corruption
issues: you can strace the server, enable this mode in the client,
and compare the sent and received data.
I'd _like_ to have this mode also log Diffie-Hellman private
exponents, session IDs, encryption and MAC keys, so that the
resulting log file could be used to independently verify the
correctness of all cryptographic operations performed by PuTTY.
However, I haven't been able to convince myself that the security
implications are acceptable. (It doesn't matter that this
information would permit an attacker to decrypt the session, because
the _already_ decrypted session is stored alongside it in the log
file. And I'm not planning, under any circumstances, to log users'
private keys. But gaining access to the log file while the session
was still running would permit an attacker to _hijack_ the session,
and that's the iffy bit.)
[originally from svn r6835]
2006-08-29 19:07:11 +00:00
|
|
|
}
|
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
static void ssh_pkt_write(Ssh ssh, PktOut *pkt)
|
|
|
|
{
|
|
|
|
if (ssh->version == 2 && ssh->v2_cbc_ignore_workaround &&
|
|
|
|
bufchain_size(&ssh->outgoing_data) != 0) {
|
|
|
|
/*
|
|
|
|
* When using a CBC-mode cipher in SSH-2, it's necessary to
|
|
|
|
* ensure that an attacker can't provide data to be encrypted
|
|
|
|
* using an IV that they know. We ensure this by prefixing
|
|
|
|
* each packet that might contain user data with an
|
|
|
|
* SSH_MSG_IGNORE.
|
|
|
|
*/
|
|
|
|
PktOut *ipkt = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_IGNORE);
|
|
|
|
put_stringz(ipkt, "");
|
2018-07-10 20:04:32 +00:00
|
|
|
ssh_bpp_format_packet(ssh->bpp, ipkt);
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ssh_bpp_format_packet(ssh->bpp, pkt);
|
|
|
|
queue_idempotent_callback(&ssh->outgoing_data_sender);
|
|
|
|
}
|
|
|
|
|
2004-11-24 19:23:02 +00:00
|
|
|
/*
|
|
|
|
* Either queue or send a packet, depending on whether queueing is
|
|
|
|
* set.
|
|
|
|
*/
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_pkt_send(Ssh ssh, PktOut *pkt)
|
2004-11-24 19:23:02 +00:00
|
|
|
{
|
2018-06-09 08:09:10 +00:00
|
|
|
if (ssh->queueing) {
|
2018-09-19 20:56:11 +00:00
|
|
|
pq_push(&ssh->outq, pkt);
|
2018-06-09 08:09:10 +00:00
|
|
|
} else {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh_pkt_write(ssh, pkt);
|
2018-06-09 08:09:10 +00:00
|
|
|
}
|
2004-11-24 19:23:02 +00:00
|
|
|
}
|
|
|
|
|
2018-06-09 08:09:10 +00:00
|
|
|
static void ssh_send_outgoing_data(void *ctx)
|
2004-11-24 19:23:02 +00:00
|
|
|
{
|
2018-06-09 08:09:10 +00:00
|
|
|
Ssh ssh = (Ssh)ctx;
|
2004-11-24 19:23:02 +00:00
|
|
|
|
2018-06-09 08:09:10 +00:00
|
|
|
while (bufchain_size(&ssh->outgoing_data) > 0) {
|
|
|
|
void *data;
|
|
|
|
int len, backlog;
|
2004-11-27 14:29:20 +00:00
|
|
|
|
2018-06-09 08:09:10 +00:00
|
|
|
bufchain_prefix(&ssh->outgoing_data, &data, &len);
|
|
|
|
backlog = s_write(ssh, data, len);
|
|
|
|
bufchain_consume(&ssh->outgoing_data, len);
|
|
|
|
|
|
|
|
ssh->outgoing_data_size += len;
|
|
|
|
if (ssh->version == 2 && !ssh->kex_in_progress &&
|
|
|
|
!ssh->bare_connection && ssh->max_data_size != 0 &&
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->outgoing_data_size > ssh->max_data_size) {
|
|
|
|
ssh->rekey_reason = "too much data sent";
|
|
|
|
ssh->rekey_class = RK_NORMAL;
|
2018-05-18 06:22:58 +00:00
|
|
|
queue_idempotent_callback(&ssh->ssh2_transport_icb);
|
2018-05-18 06:22:58 +00:00
|
|
|
}
|
2018-06-09 08:09:10 +00:00
|
|
|
|
|
|
|
if (backlog > SSH_MAX_BACKLOG) {
|
|
|
|
ssh_throttle_all(ssh, 1, backlog);
|
|
|
|
return;
|
|
|
|
}
|
2016-05-01 17:16:22 +00:00
|
|
|
}
|
2000-09-07 16:33:49 +00:00
|
|
|
}
|
|
|
|
|
2004-11-24 19:23:02 +00:00
|
|
|
/*
|
2018-06-09 08:09:10 +00:00
|
|
|
* Send all queued SSH-2 packets.
|
2004-11-24 19:23:02 +00:00
|
|
|
*/
|
|
|
|
static void ssh2_pkt_queuesend(Ssh ssh)
|
|
|
|
{
|
2018-09-19 20:56:11 +00:00
|
|
|
PktOut *pkt;
|
2004-11-24 19:23:02 +00:00
|
|
|
|
|
|
|
assert(!ssh->queueing);
|
|
|
|
|
2018-09-19 20:56:11 +00:00
|
|
|
while ((pkt = pq_pop(&ssh->outq)) != NULL)
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2004-11-24 19:23:02 +00:00
|
|
|
}
|
|
|
|
|
2000-09-07 16:33:49 +00:00
|
|
|
#if 0
|
2001-05-06 14:35:20 +00:00
|
|
|
void bndebug(char *string, Bignum b)
|
|
|
|
{
|
2000-09-07 16:33:49 +00:00
|
|
|
unsigned char *p;
|
|
|
|
int i, len;
|
|
|
|
p = ssh2_mpint_fmt(b, &len);
|
|
|
|
debug(("%s", string));
|
|
|
|
for (i = 0; i < len; i++)
|
2001-05-06 14:35:20 +00:00
|
|
|
debug((" %02x", p[i]));
|
2001-04-28 17:35:18 +00:00
|
|
|
debug(("\n"));
|
2000-12-12 10:33:13 +00:00
|
|
|
sfree(p);
|
2000-09-07 16:33:49 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2002-05-31 17:39:16 +00:00
|
|
|
/*
|
2018-05-24 09:15:36 +00:00
|
|
|
* Helper function to add an SSH-2 signature blob to a packet. Expects
|
|
|
|
* to be shown the public key blob as well as the signature blob.
|
|
|
|
* Normally just appends the sig blob unmodified as a string, except
|
|
|
|
* that it optionally breaks it open and fiddle with it to work around
|
2002-05-31 17:39:16 +00:00
|
|
|
* BUG_SSH2_RSA_PADDING.
|
|
|
|
*/
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_add_sigblob(Ssh ssh, PktOut *pkt,
|
2018-05-29 19:45:42 +00:00
|
|
|
const void *pkblob, int pkblob_len,
|
|
|
|
const void *sigblob, int sigblob_len)
|
2002-05-31 17:39:16 +00:00
|
|
|
{
|
2018-05-29 19:45:42 +00:00
|
|
|
BinarySource pk[1], sig[1];
|
|
|
|
BinarySource_BARE_INIT(pk, pkblob, pkblob_len);
|
|
|
|
BinarySource_BARE_INIT(sig, sigblob, sigblob_len);
|
2002-05-31 17:39:16 +00:00
|
|
|
|
|
|
|
/* dmemdump(pkblob, pkblob_len); */
|
|
|
|
/* dmemdump(sigblob, sigblob_len); */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See if this is in fact an ssh-rsa signature and a buggy
|
|
|
|
* server; otherwise we can just do this the easy way.
|
|
|
|
*/
|
2018-05-29 19:45:42 +00:00
|
|
|
if ((ssh->remote_bugs & BUG_SSH2_RSA_PADDING) &&
|
|
|
|
ptrlen_eq_string(get_string(pk), "ssh-rsa") &&
|
|
|
|
ptrlen_eq_string(get_string(sig), "ssh-rsa")) {
|
|
|
|
ptrlen mod_mp, sig_mp;
|
|
|
|
size_t sig_prefix_len;
|
2002-05-31 17:39:16 +00:00
|
|
|
|
|
|
|
/*
|
2018-05-29 19:45:42 +00:00
|
|
|
* Find the modulus and signature integers.
|
2002-05-31 17:39:16 +00:00
|
|
|
*/
|
2018-05-29 19:45:42 +00:00
|
|
|
get_string(pk); /* skip over exponent */
|
|
|
|
mod_mp = get_string(pk); /* remember modulus */
|
|
|
|
sig_prefix_len = sig->pos;
|
|
|
|
sig_mp = get_string(sig);
|
|
|
|
if (get_err(pk) || get_err(sig))
|
2013-07-14 10:45:54 +00:00
|
|
|
goto give_up;
|
2002-05-31 17:39:16 +00:00
|
|
|
|
2018-05-29 19:45:42 +00:00
|
|
|
/*
|
|
|
|
* Find the byte length of the modulus, not counting leading
|
|
|
|
* zeroes.
|
|
|
|
*/
|
|
|
|
while (mod_mp.len > 0 && *(const char *)mod_mp.ptr == 0) {
|
|
|
|
mod_mp.len--;
|
|
|
|
mod_mp.ptr = (const char *)mod_mp.ptr + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* debug(("modulus length is %d\n", len)); */
|
2002-05-31 17:39:16 +00:00
|
|
|
/* debug(("signature length is %d\n", siglen)); */
|
|
|
|
|
2018-05-29 19:45:42 +00:00
|
|
|
if (mod_mp.len != sig_mp.len) {
|
2018-05-24 09:15:36 +00:00
|
|
|
strbuf *substr = strbuf_new();
|
2018-05-29 19:45:42 +00:00
|
|
|
put_data(substr, sigblob, sig_prefix_len);
|
|
|
|
put_uint32(substr, mod_mp.len);
|
2018-06-09 08:01:07 +00:00
|
|
|
put_padding(substr, mod_mp.len - sig_mp.len, 0);
|
2018-05-29 19:45:42 +00:00
|
|
|
put_data(substr, sig_mp.ptr, sig_mp.len);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringsb(pkt, substr);
|
2002-05-31 17:39:16 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-07-14 10:45:54 +00:00
|
|
|
/* Otherwise fall through and do it the easy way. We also come
|
|
|
|
* here as a fallback if we discover above that the key blob
|
|
|
|
* is misformatted in some way. */
|
|
|
|
give_up:;
|
2002-05-31 17:39:16 +00:00
|
|
|
}
|
|
|
|
|
2018-05-24 09:15:36 +00:00
|
|
|
put_string(pkt, sigblob, sigblob_len);
|
2002-05-31 17:39:16 +00:00
|
|
|
}
|
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
static void ssh_feed_to_bpp(Ssh ssh)
|
|
|
|
{
|
2018-09-19 20:56:11 +00:00
|
|
|
PacketQueueNode *prev_tail = ssh->pq_full.pqb.end.prev;
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
|
|
|
|
assert(ssh->bpp);
|
|
|
|
ssh_bpp_handle_input(ssh->bpp);
|
|
|
|
|
|
|
|
if (ssh->bpp->error) {
|
|
|
|
bomb_out(ssh, ssh->bpp->error); /* also frees the error string */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ssh->bpp->seen_disconnect) {
|
|
|
|
/*
|
|
|
|
* If we've seen a DISCONNECT message, we should unset the
|
|
|
|
* close_expected flag, because now we _do_ expect the server
|
|
|
|
* to close the network connection afterwards. That way, the
|
|
|
|
* more informative connection_fatal message for the
|
|
|
|
* disconnect itself won't fight with 'Server unexpectedly
|
|
|
|
* closed network connection'.
|
|
|
|
*/
|
|
|
|
ssh->clean_exit = FALSE;
|
|
|
|
ssh->close_expected = TRUE;
|
|
|
|
ssh->disconnect_message_seen = TRUE;
|
|
|
|
}
|
|
|
|
|
2018-09-19 20:56:11 +00:00
|
|
|
if (ssh->pq_full.pqb.end.prev != prev_tail)
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
queue_idempotent_callback(&ssh->pq_full_consumer);
|
|
|
|
}
|
|
|
|
|
2018-09-19 16:37:00 +00:00
|
|
|
static void ssh_got_ssh_version(struct ssh_version_receiver *rcv,
|
|
|
|
int major_version)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2018-09-19 16:37:00 +00:00
|
|
|
Ssh ssh = FROMFIELD(rcv, struct ssh_tag, version_receiver);
|
|
|
|
BinaryPacketProtocol *old_bpp;
|
2000-10-23 10:32:37 +00:00
|
|
|
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
/*
|
2018-09-19 16:37:00 +00:00
|
|
|
* Queue an outgoing-data run: if the version string has been sent
|
|
|
|
* late rather than early, it'll still be sitting on our output
|
|
|
|
* raw data queue.
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
*/
|
2018-09-19 16:37:00 +00:00
|
|
|
queue_idempotent_callback(&ssh->outgoing_data_sender);
|
2015-11-22 14:33:28 +00:00
|
|
|
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
/*
|
2018-09-19 16:37:00 +00:00
|
|
|
* We don't support choosing a major protocol version dynamically,
|
|
|
|
* so this should always be the same value we set up in
|
|
|
|
* connect_to_host().
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
*/
|
2018-09-19 16:37:00 +00:00
|
|
|
assert(ssh->version == major_version);
|
|
|
|
|
|
|
|
old_bpp = ssh->bpp;
|
|
|
|
ssh->remote_bugs = ssh_verstring_get_bugs(old_bpp);
|
|
|
|
|
|
|
|
if (!ssh->bare_connection) {
|
|
|
|
if (ssh->version == 2) {
|
|
|
|
/*
|
|
|
|
* Retrieve both version strings from the old BPP before
|
|
|
|
* we free it.
|
|
|
|
*/
|
|
|
|
ssh->v_s = dupstr(ssh_verstring_get_remote(old_bpp));
|
|
|
|
ssh->v_c = dupstr(ssh_verstring_get_local(old_bpp));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialise SSH-2 protocol.
|
|
|
|
*/
|
|
|
|
ssh2_protocol_setup(ssh);
|
|
|
|
ssh->general_packet_processing = ssh2_general_packet_processing;
|
|
|
|
ssh->current_user_input_fn = NULL;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Initialise SSH-1 protocol.
|
|
|
|
*/
|
|
|
|
ssh1_protocol_setup(ssh);
|
|
|
|
ssh->current_user_input_fn = ssh1_login_input;
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
}
|
|
|
|
|
2018-09-19 16:37:00 +00:00
|
|
|
if (ssh->version == 2)
|
|
|
|
queue_idempotent_callback(&ssh->ssh2_transport_icb);
|
1999-11-09 11:10:04 +00:00
|
|
|
|
2016-03-28 19:23:57 +00:00
|
|
|
} else {
|
2018-09-19 16:37:00 +00:00
|
|
|
assert(ssh->version == 2); /* can't do SSH-1 bare connection! */
|
|
|
|
logeventf(ssh, "Using bare ssh-connection protocol");
|
2004-12-01 13:37:31 +00:00
|
|
|
|
2018-09-19 16:37:00 +00:00
|
|
|
ssh2_bare_connection_protocol_setup(ssh);
|
|
|
|
ssh->current_user_input_fn = ssh2_connection_input;
|
2007-07-19 23:53:02 +00:00
|
|
|
|
|
|
|
}
|
2018-09-19 16:37:00 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->bpp->out_raw = &ssh->outgoing_data;
|
|
|
|
ssh->bpp->in_raw = &ssh->incoming_data;
|
|
|
|
ssh->bpp->in_pq = &ssh->pq_full;
|
|
|
|
ssh->bpp->pls = &ssh->pls;
|
|
|
|
ssh->bpp->logctx = ssh->logctx;
|
|
|
|
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
queue_idempotent_callback(&ssh->incoming_data_consumer);
|
2018-05-18 10:40:21 +00:00
|
|
|
queue_idempotent_callback(&ssh->user_input_consumer);
|
2007-07-19 23:53:02 +00:00
|
|
|
|
2003-04-04 20:21:05 +00:00
|
|
|
update_specials_menu(ssh->frontend);
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->state = SSH_STATE_BEFORE_SIZE;
|
2018-09-11 15:23:38 +00:00
|
|
|
ssh->pinger = pinger_new(ssh->conf, &ssh->backend);
|
2000-10-23 10:32:37 +00:00
|
|
|
|
2018-09-19 16:37:00 +00:00
|
|
|
if (ssh->bare_connection) {
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
/*
|
2018-09-19 16:37:00 +00:00
|
|
|
* Get connection protocol under way.
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
*/
|
2018-09-19 16:37:00 +00:00
|
|
|
do_ssh2_connection(ssh);
|
2013-11-17 14:05:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
static void ssh_process_incoming_data(void *ctx)
|
2005-02-17 18:34:24 +00:00
|
|
|
{
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
Ssh ssh = (Ssh)ctx;
|
2005-02-17 18:34:24 +00:00
|
|
|
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
if (ssh->state == SSH_STATE_CLOSED)
|
|
|
|
return;
|
2005-02-17 18:34:24 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
if (!ssh->frozen)
|
2018-09-19 16:37:00 +00:00
|
|
|
ssh_feed_to_bpp(ssh);
|
2005-02-17 18:34:24 +00:00
|
|
|
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
if (ssh->state == SSH_STATE_CLOSED) /* yes, check _again_ */
|
|
|
|
return;
|
2005-02-17 18:34:24 +00:00
|
|
|
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
if (ssh->incoming_data_seen_eof) {
|
|
|
|
int need_notify = ssh_do_close(ssh, FALSE);
|
|
|
|
const char *error_msg = ssh->incoming_data_eof_message;
|
2005-02-17 18:34:24 +00:00
|
|
|
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
if (!error_msg) {
|
|
|
|
if (!ssh->close_expected)
|
|
|
|
error_msg = "Server unexpectedly closed network connection";
|
|
|
|
else
|
|
|
|
error_msg = "Server closed network connection";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ssh->close_expected && ssh->clean_exit && ssh->exitcode < 0)
|
|
|
|
ssh->exitcode = 0;
|
|
|
|
|
|
|
|
if (need_notify)
|
|
|
|
notify_remote_exit(ssh->frontend);
|
|
|
|
|
|
|
|
if (error_msg)
|
|
|
|
logevent(error_msg);
|
2018-06-03 05:46:28 +00:00
|
|
|
if ((!ssh->close_expected || !ssh->clean_exit) &&
|
|
|
|
!ssh->disconnect_message_seen)
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
connection_fatal(ssh->frontend, "%s", error_msg);
|
|
|
|
}
|
2005-02-17 18:34:24 +00:00
|
|
|
}
|
|
|
|
|
Make the rdpkt functions output to a PacketQueue.
Each of the coroutines that parses the incoming wire data into a
stream of 'struct Packet' now delivers those packets to a PacketQueue
called ssh->pq_full (containing the full, unfiltered stream of all
packets received on the SSH connection), replacing the old API in
which each coroutine would directly return a 'struct Packet *' to its
caller, or NULL if it didn't have one ready yet.
This simplifies the function-call API of the rdpkt coroutines (they
now return void). It increases the complexity at the other end,
because we've now got a function ssh_process_pq_full (scheduled as an
idempotent callback whenever rdpkt appends anything to the queue)
which pulls things out of the queue and passes them to ssh->protocol.
But that's only a temporary complexity increase; by the time I finish
the upcoming stream of refactorings, there won't be two chained
functions there any more.
One small workaround I had to add in this commit is a flag called
'pending_newkeys', which ssh2_rdpkt sets when it's just returned an
SSH_MSG_NEWKEYS packet, and then waits for the transport layer to
process the NEWKEYS and set up the new encryption context before
processing any more wire data. This wasn't necessary before, because
the old architecture was naturally synchronous - ssh2_rdpkt would
return a NEWKEYS, which would be immediately passed to
do_ssh2_transport, which would finish processing it immediately, and
by the time ssh2_rdpkt was next called, the keys would already be in
place.
This change adds a big while loop around the whole of each rdpkt
function, so it's easiest to read it as a whitespace-ignored diff.
2018-05-18 06:22:57 +00:00
|
|
|
static void ssh_process_pq_full(void *ctx)
|
|
|
|
{
|
|
|
|
Ssh ssh = (Ssh)ctx;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktIn *pktin;
|
Make the rdpkt functions output to a PacketQueue.
Each of the coroutines that parses the incoming wire data into a
stream of 'struct Packet' now delivers those packets to a PacketQueue
called ssh->pq_full (containing the full, unfiltered stream of all
packets received on the SSH connection), replacing the old API in
which each coroutine would directly return a 'struct Packet *' to its
caller, or NULL if it didn't have one ready yet.
This simplifies the function-call API of the rdpkt coroutines (they
now return void). It increases the complexity at the other end,
because we've now got a function ssh_process_pq_full (scheduled as an
idempotent callback whenever rdpkt appends anything to the queue)
which pulls things out of the queue and passes them to ssh->protocol.
But that's only a temporary complexity increase; by the time I finish
the upcoming stream of refactorings, there won't be two chained
functions there any more.
One small workaround I had to add in this commit is a flag called
'pending_newkeys', which ssh2_rdpkt sets when it's just returned an
SSH_MSG_NEWKEYS packet, and then waits for the transport layer to
process the NEWKEYS and set up the new encryption context before
processing any more wire data. This wasn't necessary before, because
the old architecture was naturally synchronous - ssh2_rdpkt would
return a NEWKEYS, which would be immediately passed to
do_ssh2_transport, which would finish processing it immediately, and
by the time ssh2_rdpkt was next called, the keys would already be in
place.
This change adds a big while loop around the whole of each rdpkt
function, so it's easiest to read it as a whitespace-ignored diff.
2018-05-18 06:22:57 +00:00
|
|
|
|
|
|
|
while ((pktin = pq_pop(&ssh->pq_full)) != NULL) {
|
2018-05-18 06:22:57 +00:00
|
|
|
if (ssh->general_packet_processing)
|
|
|
|
ssh->general_packet_processing(ssh, pktin);
|
|
|
|
ssh->packet_dispatch[pktin->type](ssh, pktin);
|
Make the rdpkt functions output to a PacketQueue.
Each of the coroutines that parses the incoming wire data into a
stream of 'struct Packet' now delivers those packets to a PacketQueue
called ssh->pq_full (containing the full, unfiltered stream of all
packets received on the SSH connection), replacing the old API in
which each coroutine would directly return a 'struct Packet *' to its
caller, or NULL if it didn't have one ready yet.
This simplifies the function-call API of the rdpkt coroutines (they
now return void). It increases the complexity at the other end,
because we've now got a function ssh_process_pq_full (scheduled as an
idempotent callback whenever rdpkt appends anything to the queue)
which pulls things out of the queue and passes them to ssh->protocol.
But that's only a temporary complexity increase; by the time I finish
the upcoming stream of refactorings, there won't be two chained
functions there any more.
One small workaround I had to add in this commit is a flag called
'pending_newkeys', which ssh2_rdpkt sets when it's just returned an
SSH_MSG_NEWKEYS packet, and then waits for the transport layer to
process the NEWKEYS and set up the new encryption context before
processing any more wire data. This wasn't necessary before, because
the old architecture was naturally synchronous - ssh2_rdpkt would
return a NEWKEYS, which would be immediately passed to
do_ssh2_transport, which would finish processing it immediately, and
by the time ssh2_rdpkt was next called, the keys would already be in
place.
This change adds a big while loop around the whole of each rdpkt
function, so it's easiest to read it as a whitespace-ignored diff.
2018-05-18 06:22:57 +00:00
|
|
|
ssh_unref_packet(pktin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
static void ssh_process_user_input(void *ctx)
|
|
|
|
{
|
|
|
|
Ssh ssh = (Ssh)ctx;
|
|
|
|
if (ssh->current_user_input_fn)
|
|
|
|
ssh->current_user_input_fn(ssh);
|
|
|
|
}
|
|
|
|
|
2005-01-11 19:33:41 +00:00
|
|
|
static int ssh_do_close(Ssh ssh, int notify_exit)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2005-02-17 01:47:01 +00:00
|
|
|
int ret = 0;
|
2003-04-25 11:48:24 +00:00
|
|
|
struct ssh_channel *c;
|
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->state = SSH_STATE_CLOSED;
|
2005-03-30 08:27:54 +00:00
|
|
|
expire_timer_context(ssh);
|
2002-10-25 11:30:33 +00:00
|
|
|
if (ssh->s) {
|
|
|
|
sk_close(ssh->s);
|
|
|
|
ssh->s = NULL;
|
2005-01-11 19:33:41 +00:00
|
|
|
if (notify_exit)
|
|
|
|
notify_remote_exit(ssh->frontend);
|
|
|
|
else
|
|
|
|
ret = 1;
|
2001-07-31 14:23:21 +00:00
|
|
|
}
|
2003-04-25 11:48:24 +00:00
|
|
|
/*
|
2005-02-17 01:47:01 +00:00
|
|
|
* Now we must shut down any port- and X-forwarded channels going
|
2003-04-25 11:48:24 +00:00
|
|
|
* through this connection.
|
|
|
|
*/
|
2003-04-25 17:44:09 +00:00
|
|
|
if (ssh->channels) {
|
2005-02-17 01:47:01 +00:00
|
|
|
while (NULL != (c = index234(ssh->channels, 0))) {
|
2016-05-28 13:50:02 +00:00
|
|
|
ssh_channel_close_local(c, NULL);
|
2005-02-17 01:47:01 +00:00
|
|
|
del234(ssh->channels, c); /* moving next one to index 0 */
|
2003-04-25 17:44:09 +00:00
|
|
|
if (ssh->version == 2)
|
|
|
|
bufchain_clear(&c->v.v2.outbuffer);
|
|
|
|
sfree(c);
|
2003-04-25 11:48:24 +00:00
|
|
|
}
|
|
|
|
}
|
2005-02-17 02:00:01 +00:00
|
|
|
/*
|
|
|
|
* Go through port-forwardings, and close any associated
|
|
|
|
* listening sockets.
|
|
|
|
*/
|
2018-09-14 16:04:39 +00:00
|
|
|
portfwdmgr_close_all(ssh->portfwdmgr);
|
2005-01-11 19:33:41 +00:00
|
|
|
|
2014-11-10 18:29:00 +00:00
|
|
|
/*
|
|
|
|
* Also stop attempting to connection-share.
|
|
|
|
*/
|
|
|
|
if (ssh->connshare) {
|
|
|
|
sharestate_free(ssh->connshare);
|
|
|
|
ssh->connshare = NULL;
|
|
|
|
}
|
|
|
|
|
2005-01-11 19:33:41 +00:00
|
|
|
return ret;
|
2003-04-25 11:48:24 +00:00
|
|
|
}
|
|
|
|
|
2013-11-17 14:05:41 +00:00
|
|
|
static void ssh_socket_log(Plug plug, int type, SockAddr addr, int port,
|
|
|
|
const char *error_msg, int error_code)
|
2005-01-16 14:29:34 +00:00
|
|
|
{
|
2018-05-27 08:29:33 +00:00
|
|
|
Ssh ssh = FROMFIELD(plug, struct ssh_tag, plugvt);
|
2005-01-16 14:29:34 +00:00
|
|
|
|
2015-11-22 11:49:14 +00:00
|
|
|
/*
|
|
|
|
* While we're attempting connection sharing, don't loudly log
|
|
|
|
* everything that happens. Real TCP connections need to be logged
|
|
|
|
* when we _start_ trying to connect, because it might be ages
|
|
|
|
* before they respond if something goes wrong; but connection
|
|
|
|
* sharing is local and quick to respond, and it's sufficient to
|
|
|
|
* simply wait and see whether it worked afterwards.
|
|
|
|
*/
|
2013-11-17 14:05:41 +00:00
|
|
|
|
2015-11-22 11:49:14 +00:00
|
|
|
if (!ssh->attempting_connshare)
|
|
|
|
backend_socket_log(ssh->frontend, type, addr, port,
|
2015-11-22 14:33:28 +00:00
|
|
|
error_msg, error_code, ssh->conf,
|
|
|
|
ssh->session_started);
|
2013-11-17 14:05:41 +00:00
|
|
|
}
|
2005-01-16 14:29:34 +00:00
|
|
|
|
2016-06-02 22:03:24 +00:00
|
|
|
static void ssh_closing(Plug plug, const char *error_msg, int error_code,
|
|
|
|
int calling_back)
|
2003-04-25 11:48:24 +00:00
|
|
|
{
|
2018-05-27 08:29:33 +00:00
|
|
|
Ssh ssh = FROMFIELD(plug, struct ssh_tag, plugvt);
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
ssh->incoming_data_seen_eof = TRUE;
|
|
|
|
ssh->incoming_data_eof_message = dupstr(error_msg);
|
|
|
|
queue_idempotent_callback(&ssh->incoming_data_consumer);
|
2001-03-13 10:22:45 +00:00
|
|
|
}
|
|
|
|
|
2016-06-02 22:03:24 +00:00
|
|
|
static void ssh_receive(Plug plug, int urgent, char *data, int len)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2018-05-27 08:29:33 +00:00
|
|
|
Ssh ssh = FROMFIELD(plug, struct ssh_tag, plugvt);
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
|
|
|
|
/* Log raw data, if we're in that mode. */
|
|
|
|
if (ssh->logctx)
|
|
|
|
log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, len,
|
|
|
|
0, NULL, NULL, 0, NULL);
|
|
|
|
|
|
|
|
bufchain_add(&ssh->incoming_data, data, len);
|
|
|
|
queue_idempotent_callback(&ssh->incoming_data_consumer);
|
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
if (ssh->state == SSH_STATE_CLOSED) {
|
2005-01-11 19:33:41 +00:00
|
|
|
ssh_do_close(ssh, TRUE);
|
2000-10-24 09:55:45 +00:00
|
|
|
}
|
1999-01-08 13:02:13 +00:00
|
|
|
}
|
|
|
|
|
2001-08-25 17:09:23 +00:00
|
|
|
static void ssh_sent(Plug plug, int bufsize)
|
|
|
|
{
|
2018-05-27 08:29:33 +00:00
|
|
|
Ssh ssh = FROMFIELD(plug, struct ssh_tag, plugvt);
|
2001-08-25 17:09:23 +00:00
|
|
|
/*
|
2018-06-13 18:44:44 +00:00
|
|
|
* If the send backlog on the SSH socket itself clears, we should
|
|
|
|
* unthrottle the whole world if it was throttled, and also resume
|
|
|
|
* sending our bufchain of queued wire data.
|
2001-08-25 17:09:23 +00:00
|
|
|
*/
|
2018-06-13 18:44:44 +00:00
|
|
|
if (bufsize < SSH_MAX_BACKLOG) {
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh_throttle_all(ssh, 0, bufsize);
|
2018-06-13 18:44:44 +00:00
|
|
|
queue_idempotent_callback(&ssh->outgoing_data_sender);
|
|
|
|
}
|
2001-08-25 17:09:23 +00:00
|
|
|
}
|
|
|
|
|
2015-09-25 09:58:29 +00:00
|
|
|
static void ssh_hostport_setup(const char *host, int port, Conf *conf,
|
|
|
|
char **savedhost, int *savedport,
|
|
|
|
char **loghost_ret)
|
2000-10-23 10:32:37 +00:00
|
|
|
{
|
2015-09-25 09:58:29 +00:00
|
|
|
char *loghost = conf_get_str(conf, CONF_loghost);
|
|
|
|
if (loghost_ret)
|
|
|
|
*loghost_ret = loghost;
|
2001-03-13 10:22:45 +00:00
|
|
|
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (*loghost) {
|
2014-01-25 15:58:54 +00:00
|
|
|
char *tmphost;
|
|
|
|
char *colon;
|
2000-10-23 10:32:37 +00:00
|
|
|
|
2014-01-25 15:58:54 +00:00
|
|
|
tmphost = dupstr(loghost);
|
2015-09-25 09:58:29 +00:00
|
|
|
*savedport = 22; /* default ssh port */
|
2008-06-01 11:16:32 +00:00
|
|
|
|
|
|
|
/*
|
2014-01-25 15:58:54 +00:00
|
|
|
* A colon suffix on the hostname string also lets us affect
|
2014-01-25 15:58:57 +00:00
|
|
|
* savedport. (Unless there are multiple colons, in which case
|
|
|
|
* we assume this is an unbracketed IPv6 literal.)
|
2008-06-01 11:16:32 +00:00
|
|
|
*/
|
2014-01-25 15:58:54 +00:00
|
|
|
colon = host_strrchr(tmphost, ':');
|
2014-01-25 15:58:57 +00:00
|
|
|
if (colon && colon == host_strchr(tmphost, ':')) {
|
2008-06-01 11:16:32 +00:00
|
|
|
*colon++ = '\0';
|
|
|
|
if (*colon)
|
2015-09-25 09:58:29 +00:00
|
|
|
*savedport = atoi(colon);
|
2008-06-01 11:16:32 +00:00
|
|
|
}
|
2014-01-25 15:58:54 +00:00
|
|
|
|
2015-09-25 09:58:29 +00:00
|
|
|
*savedhost = host_strduptrim(tmphost);
|
2014-01-25 15:58:54 +00:00
|
|
|
sfree(tmphost);
|
2008-06-01 11:16:32 +00:00
|
|
|
} else {
|
2015-09-25 09:58:29 +00:00
|
|
|
*savedhost = host_strduptrim(host);
|
2008-06-01 11:16:32 +00:00
|
|
|
if (port < 0)
|
|
|
|
port = 22; /* default ssh port */
|
2015-09-25 09:58:29 +00:00
|
|
|
*savedport = port;
|
2008-06-01 11:16:32 +00:00
|
|
|
}
|
2015-09-25 09:58:29 +00:00
|
|
|
}
|
|
|
|
|
2015-09-25 10:46:28 +00:00
|
|
|
static int ssh_test_for_upstream(const char *host, int port, Conf *conf)
|
|
|
|
{
|
|
|
|
char *savedhost;
|
|
|
|
int savedport;
|
|
|
|
int ret;
|
|
|
|
|
2015-10-24 15:44:37 +00:00
|
|
|
random_ref(); /* platform may need this to determine share socket name */
|
2015-09-25 10:46:28 +00:00
|
|
|
ssh_hostport_setup(host, port, conf, &savedhost, &savedport, NULL);
|
|
|
|
ret = ssh_share_test_for_upstream(savedhost, savedport, conf);
|
|
|
|
sfree(savedhost);
|
2015-10-24 15:44:37 +00:00
|
|
|
random_unref();
|
2015-09-25 10:46:28 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-05-27 08:29:33 +00:00
|
|
|
static const Plug_vtable Ssh_plugvt = {
|
|
|
|
ssh_socket_log,
|
|
|
|
ssh_closing,
|
|
|
|
ssh_receive,
|
|
|
|
ssh_sent,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2015-09-25 09:58:29 +00:00
|
|
|
/*
|
|
|
|
* Connect to specified host and port.
|
|
|
|
* Returns an error message, or NULL on success.
|
|
|
|
* Also places the canonical host name into `realhost'. It must be
|
|
|
|
* freed by the caller.
|
|
|
|
*/
|
|
|
|
static const char *connect_to_host(Ssh ssh, const char *host, int port,
|
|
|
|
char **realhost, int nodelay, int keepalive)
|
|
|
|
{
|
|
|
|
SockAddr addr;
|
|
|
|
const char *err;
|
|
|
|
char *loghost;
|
|
|
|
int addressfamily, sshprot;
|
|
|
|
|
|
|
|
ssh_hostport_setup(host, port, ssh->conf,
|
|
|
|
&ssh->savedhost, &ssh->savedport, &loghost);
|
2000-10-23 10:32:37 +00:00
|
|
|
|
2018-05-27 08:29:33 +00:00
|
|
|
ssh->plugvt = &Ssh_plugvt;
|
2000-10-23 10:32:37 +00:00
|
|
|
|
|
|
|
/*
|
2013-11-17 14:05:41 +00:00
|
|
|
* Try connection-sharing, in case that means we don't open a
|
|
|
|
* socket after all. ssh_connection_sharing_init will connect to a
|
|
|
|
* previously established upstream if it can, and failing that,
|
|
|
|
* establish a listening socket for _us_ to be the upstream. In
|
|
|
|
* the latter case it will return NULL just as if it had done
|
|
|
|
* nothing, because here we only need to care if we're a
|
|
|
|
* downstream and need to do our connection setup differently.
|
2000-10-23 10:32:37 +00:00
|
|
|
*/
|
2013-11-17 14:05:41 +00:00
|
|
|
ssh->connshare = NULL;
|
|
|
|
ssh->attempting_connshare = TRUE; /* affects socket logging behaviour */
|
2018-05-27 08:29:33 +00:00
|
|
|
ssh->s = ssh_connection_sharing_init(
|
2018-09-17 11:14:00 +00:00
|
|
|
ssh->savedhost, ssh->savedport, ssh->conf, &ssh->cl, &ssh->plugvt,
|
2018-05-27 08:29:33 +00:00
|
|
|
&ssh->connshare);
|
2013-11-17 14:05:41 +00:00
|
|
|
ssh->attempting_connshare = FALSE;
|
|
|
|
if (ssh->s != NULL) {
|
|
|
|
/*
|
|
|
|
* We are a downstream.
|
|
|
|
*/
|
|
|
|
ssh->bare_connection = TRUE;
|
|
|
|
ssh->fullhostname = NULL;
|
|
|
|
*realhost = dupstr(host); /* best we can do */
|
2018-09-17 11:14:00 +00:00
|
|
|
|
|
|
|
if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
|
|
|
|
/* In an interactive session, or in verbose mode, announce
|
|
|
|
* in the console window that we're a sharing downstream,
|
|
|
|
* to avoid confusing users as to why this session doesn't
|
|
|
|
* behave in quite the usual way. */
|
|
|
|
c_write_str(ssh,"Reusing a shared connection to this server.\r\n");
|
|
|
|
}
|
2013-11-17 14:05:41 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We're not a downstream, so open a normal socket.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to find host.
|
|
|
|
*/
|
|
|
|
addressfamily = conf_get_int(ssh->conf, CONF_addressfamily);
|
2015-11-22 09:58:14 +00:00
|
|
|
addr = name_lookup(host, port, realhost, ssh->conf, addressfamily,
|
|
|
|
ssh->frontend, "SSH connection");
|
2013-11-17 14:05:41 +00:00
|
|
|
if ((err = sk_addr_error(addr)) != NULL) {
|
|
|
|
sk_addr_free(addr);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
ssh->fullhostname = dupstr(*realhost); /* save in case of GSSAPI */
|
|
|
|
|
|
|
|
ssh->s = new_connection(addr, *realhost, port,
|
|
|
|
0, 1, nodelay, keepalive,
|
2018-05-27 08:29:33 +00:00
|
|
|
&ssh->plugvt, ssh->conf);
|
2013-11-17 14:05:41 +00:00
|
|
|
if ((err = sk_socket_error(ssh->s)) != NULL) {
|
|
|
|
ssh->s = NULL;
|
|
|
|
notify_remote_exit(ssh->frontend);
|
|
|
|
return err;
|
|
|
|
}
|
2002-02-27 22:45:52 +00:00
|
|
|
}
|
2000-10-23 10:32:37 +00:00
|
|
|
|
2007-07-21 13:43:57 +00:00
|
|
|
/*
|
2016-03-28 19:23:57 +00:00
|
|
|
* The SSH version number is always fixed (since we no longer support
|
2018-09-19 16:37:00 +00:00
|
|
|
* fallback between versions), so set it now.
|
2007-07-21 13:43:57 +00:00
|
|
|
*/
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
sshprot = conf_get_int(ssh->conf, CONF_sshprot);
|
2016-03-28 19:23:57 +00:00
|
|
|
assert(sshprot == 0 || sshprot == 3);
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (sshprot == 0)
|
2016-03-28 19:23:57 +00:00
|
|
|
/* SSH-1 only */
|
2007-07-21 13:43:57 +00:00
|
|
|
ssh->version = 1;
|
2018-09-19 16:37:00 +00:00
|
|
|
if (sshprot == 3 || ssh->bare_connection) {
|
2016-03-28 19:23:57 +00:00
|
|
|
/* SSH-2 only */
|
2007-07-21 13:43:57 +00:00
|
|
|
ssh->version = 2;
|
|
|
|
}
|
|
|
|
|
2018-09-19 16:37:00 +00:00
|
|
|
/*
|
|
|
|
* Set up the initial BPP that will do the version string
|
|
|
|
* exchange.
|
|
|
|
*/
|
|
|
|
ssh->version_receiver.got_ssh_version = ssh_got_ssh_version;
|
|
|
|
ssh->bpp = ssh_verstring_new(
|
|
|
|
ssh->conf, ssh->frontend, ssh->bare_connection,
|
|
|
|
ssh->version == 1 ? "1.5" : "2.0", &ssh->version_receiver);
|
|
|
|
ssh->bpp->out_raw = &ssh->outgoing_data;
|
|
|
|
ssh->bpp->in_raw = &ssh->incoming_data;
|
|
|
|
/*
|
|
|
|
* And call its handle_input method right now, in case it wants to
|
|
|
|
* send the outgoing version string immediately.
|
|
|
|
*/
|
|
|
|
ssh_bpp_handle_input(ssh->bpp);
|
|
|
|
queue_idempotent_callback(&ssh->outgoing_data_sender);
|
|
|
|
|
2008-06-01 11:16:32 +00:00
|
|
|
/*
|
|
|
|
* loghost, if configured, overrides realhost.
|
|
|
|
*/
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (*loghost) {
|
2008-06-01 11:16:32 +00:00
|
|
|
sfree(*realhost);
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
*realhost = dupstr(loghost);
|
2008-06-01 11:16:32 +00:00
|
|
|
}
|
|
|
|
|
2000-10-23 10:32:37 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2001-08-25 17:09:23 +00:00
|
|
|
/*
|
|
|
|
* Throttle or unthrottle the SSH connection.
|
|
|
|
*/
|
2007-10-03 20:29:27 +00:00
|
|
|
static void ssh_throttle_conn(Ssh ssh, int adjust)
|
2001-08-25 17:09:23 +00:00
|
|
|
{
|
2007-10-03 20:29:27 +00:00
|
|
|
int old_count = ssh->conn_throttle_count;
|
2018-09-19 19:52:12 +00:00
|
|
|
int frozen;
|
|
|
|
|
2007-10-03 20:29:27 +00:00
|
|
|
ssh->conn_throttle_count += adjust;
|
|
|
|
assert(ssh->conn_throttle_count >= 0);
|
2018-09-19 19:52:12 +00:00
|
|
|
|
2007-10-03 20:29:27 +00:00
|
|
|
if (ssh->conn_throttle_count && !old_count) {
|
2018-09-19 19:52:12 +00:00
|
|
|
frozen = TRUE;
|
2007-10-03 20:29:27 +00:00
|
|
|
} else if (!ssh->conn_throttle_count && old_count) {
|
2018-09-19 19:52:12 +00:00
|
|
|
frozen = FALSE;
|
|
|
|
} else {
|
|
|
|
return; /* don't change current frozen state */
|
2001-08-25 17:09:23 +00:00
|
|
|
}
|
2018-09-19 19:52:12 +00:00
|
|
|
|
|
|
|
ssh->frozen = frozen;
|
|
|
|
|
|
|
|
if (ssh->s)
|
|
|
|
sk_set_frozen(ssh->s, frozen);
|
2001-08-25 17:09:23 +00:00
|
|
|
}
|
|
|
|
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
static void ssh_channel_check_throttle(struct ssh_channel *c)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We don't want this channel to read further input if this
|
|
|
|
* particular channel has a backed-up SSH window, or if the
|
|
|
|
* outgoing side of the whole SSH connection is currently
|
|
|
|
* throttled, or if this channel already has an outgoing EOF
|
|
|
|
* either sent or pending.
|
|
|
|
*/
|
|
|
|
chan_set_input_wanted(c->chan,
|
|
|
|
!c->throttled_by_backlog &&
|
|
|
|
!c->ssh->throttled_all &&
|
|
|
|
!c->pending_eof &&
|
|
|
|
!(c->closes & CLOSES_SENT_EOF));
|
|
|
|
}
|
Rewrite agent forwarding to serialise requests.
The previous agent-forwarding system worked by passing each complete
query received from the input to agent_query() as soon as it was
ready. So if the remote client were to pipeline multiple requests,
then Unix PuTTY (in which agent_query() works asynchronously) would
parallelise them into many _simultaneous_ connections to the real
agent - and would not track which query went out first, so that if the
real agent happened to send its replies (to what _it_ thought were
independent clients) in the wrong order, then PuTTY would serialise
the replies on to the forwarding channel in whatever order it got
them, which wouldn't be the order the remote client was expecting.
To solve this, I've done a considerable rewrite, which keeps the
request stream in a bufchain, and only removes data from the bufchain
when it has a complete request. Then, if agent_query decides to be
asynchronous, the forwarding system waits for _that_ agent response
before even trying to extract the next request's worth of data from
the bufchain.
As an added bonus (in principle), this gives agent-forwarding channels
some actual flow control for the first time ever! If a client spams us
with an endless stream of rapid requests, and never reads its
responses, then the output side of the channel will run out of window,
which causes us to stop processing requests until we have space to
send responses again, which in turn causes us to stop granting extra
window on the input side, which serves the client right.
2017-01-29 19:40:38 +00:00
|
|
|
|
2001-08-25 17:09:23 +00:00
|
|
|
/*
|
|
|
|
* Throttle or unthrottle _all_ local data streams (for when sends
|
|
|
|
* on the SSH connection itself back up).
|
|
|
|
*/
|
2002-10-25 11:30:33 +00:00
|
|
|
static void ssh_throttle_all(Ssh ssh, int enable, int bufsize)
|
2001-08-25 17:09:23 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct ssh_channel *c;
|
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
if (enable == ssh->throttled_all)
|
2001-08-25 17:09:23 +00:00
|
|
|
return;
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->throttled_all = enable;
|
|
|
|
ssh->overall_bufsize = bufsize;
|
|
|
|
if (!ssh->channels)
|
2001-08-25 17:09:23 +00:00
|
|
|
return;
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
for (i = 0; NULL != (c = index234(ssh->channels, i)); i++)
|
|
|
|
ssh_channel_check_throttle(c);
|
2001-08-25 17:09:23 +00:00
|
|
|
}
|
|
|
|
|
2003-05-10 12:54:29 +00:00
|
|
|
static void ssh_agent_callback(void *sshv, void *reply, int replylen)
|
2003-04-28 11:41:39 +00:00
|
|
|
{
|
|
|
|
Ssh ssh = (Ssh) sshv;
|
|
|
|
|
2017-01-29 20:24:15 +00:00
|
|
|
ssh->auth_agent_query = NULL;
|
|
|
|
|
2003-04-28 11:41:39 +00:00
|
|
|
ssh->agent_response = reply;
|
|
|
|
ssh->agent_response_len = replylen;
|
|
|
|
|
|
|
|
if (ssh->version == 1)
|
2018-05-18 06:22:58 +00:00
|
|
|
do_ssh1_login(ssh);
|
2003-04-28 11:41:39 +00:00
|
|
|
else
|
2018-05-18 06:22:59 +00:00
|
|
|
do_ssh2_userauth(ssh);
|
2003-04-28 11:41:39 +00:00
|
|
|
}
|
|
|
|
|
2005-02-17 18:34:24 +00:00
|
|
|
static void ssh_dialog_callback(void *sshv, int ret)
|
|
|
|
{
|
|
|
|
Ssh ssh = (Ssh) sshv;
|
|
|
|
|
|
|
|
ssh->user_response = ret;
|
|
|
|
|
|
|
|
if (ssh->version == 1)
|
2018-05-18 06:22:58 +00:00
|
|
|
do_ssh1_login(ssh);
|
2005-02-17 18:34:24 +00:00
|
|
|
else
|
2018-05-18 06:22:58 +00:00
|
|
|
queue_idempotent_callback(&ssh->ssh2_transport_icb);
|
2005-02-17 18:34:24 +00:00
|
|
|
|
|
|
|
/*
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
* This may have unfrozen the SSH connection.
|
2005-02-17 18:34:24 +00:00
|
|
|
*/
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
if (!ssh->frozen)
|
|
|
|
queue_idempotent_callback(&ssh->incoming_data_consumer);
|
2005-02-17 18:34:24 +00:00
|
|
|
}
|
|
|
|
|
2005-05-21 16:49:27 +00:00
|
|
|
/*
|
|
|
|
* Client-initiated disconnection. Send a DISCONNECT if `wire_reason'
|
|
|
|
* non-NULL, otherwise just close the connection. `client_reason' == NULL
|
|
|
|
* => log `wire_reason'.
|
|
|
|
*/
|
2015-05-15 10:15:42 +00:00
|
|
|
static void ssh_disconnect(Ssh ssh, const char *client_reason,
|
|
|
|
const char *wire_reason,
|
2005-05-21 16:49:27 +00:00
|
|
|
int code, int clean_exit)
|
|
|
|
{
|
|
|
|
char *error;
|
|
|
|
if (!client_reason)
|
|
|
|
client_reason = wire_reason;
|
|
|
|
if (client_reason)
|
|
|
|
error = dupprintf("Disconnected: %s", client_reason);
|
|
|
|
else
|
|
|
|
error = dupstr("Disconnected");
|
|
|
|
if (wire_reason) {
|
|
|
|
if (ssh->version == 1) {
|
2018-06-09 08:59:58 +00:00
|
|
|
PktOut *pktout = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_DISCONNECT);
|
|
|
|
put_stringz(pktout, wire_reason);
|
|
|
|
ssh_pkt_write(ssh, pktout);
|
2005-05-21 16:49:27 +00:00
|
|
|
} else if (ssh->version == 2) {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
PktOut *pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_DISCONNECT);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(pktout, code);
|
|
|
|
put_stringz(pktout, wire_reason);
|
|
|
|
put_stringz(pktout, "en"); /* language tag */
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh_pkt_write(ssh, pktout);
|
2005-05-21 16:49:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ssh->close_expected = TRUE;
|
|
|
|
ssh->clean_exit = clean_exit;
|
2018-05-27 08:29:33 +00:00
|
|
|
ssh_closing(&ssh->plugvt, error, 0, 0);
|
2005-05-21 16:49:27 +00:00
|
|
|
sfree(error);
|
|
|
|
}
|
|
|
|
|
2018-06-03 11:58:05 +00:00
|
|
|
int verify_ssh_manual_host_key(Ssh ssh, const char *fingerprint, ssh_key *key)
|
New option to manually configure the expected host key(s).
This option is available from the command line as '-hostkey', and is
also configurable through the GUI. When enabled, it completely
replaces all of the automated host key management: the server's host
key will be checked against the manually configured list, and the
connection will be allowed or disconnected on that basis, and the host
key store in the registry will not be either consulted or updated.
The main aim is to provide a means of automatically running Plink,
PSCP or PSFTP deep inside Windows services where HKEY_CURRENT_USER
isn't available to have stored the right host key in. But it also
permits you to specify a list of multiple host keys, which means a
second use case for the same mechanism will probably be round-robin
DNS names that select one of several servers with different host keys.
Host keys can be specified as the standard MD5 fingerprint or as an
SSH-2 base64 blob, and are canonicalised on input. (The base64 blob is
more unwieldy, especially with Windows command-line length limits, but
provides a means of specifying the _whole_ public key in case you
don't trust MD5. I haven't bothered to provide an analogous mechanism
for SSH-1, on the basis that anyone worrying about MD5 should have
stopped using SSH-1 already!)
[originally from svn r10220]
2014-09-09 11:46:24 +00:00
|
|
|
{
|
|
|
|
if (!conf_get_str_nthstrkey(ssh->conf, CONF_ssh_manual_hostkeys, 0)) {
|
|
|
|
return -1; /* no manual keys configured */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fingerprint) {
|
|
|
|
/*
|
|
|
|
* The fingerprint string we've been given will have things
|
|
|
|
* like 'ssh-rsa 2048' at the front of it. Strip those off and
|
|
|
|
* narrow down to just the colon-separated hex block at the
|
|
|
|
* end of the string.
|
|
|
|
*/
|
|
|
|
const char *p = strrchr(fingerprint, ' ');
|
|
|
|
fingerprint = p ? p+1 : fingerprint;
|
|
|
|
/* Quick sanity checks, including making sure it's in lowercase */
|
|
|
|
assert(strlen(fingerprint) == 16*3 - 1);
|
|
|
|
assert(fingerprint[2] == ':');
|
|
|
|
assert(fingerprint[strspn(fingerprint, "0123456789abcdef:")] == 0);
|
|
|
|
|
|
|
|
if (conf_get_str_str_opt(ssh->conf, CONF_ssh_manual_hostkeys,
|
|
|
|
fingerprint))
|
|
|
|
return 1; /* success */
|
|
|
|
}
|
|
|
|
|
2018-06-03 11:58:05 +00:00
|
|
|
if (key) {
|
New option to manually configure the expected host key(s).
This option is available from the command line as '-hostkey', and is
also configurable through the GUI. When enabled, it completely
replaces all of the automated host key management: the server's host
key will be checked against the manually configured list, and the
connection will be allowed or disconnected on that basis, and the host
key store in the registry will not be either consulted or updated.
The main aim is to provide a means of automatically running Plink,
PSCP or PSFTP deep inside Windows services where HKEY_CURRENT_USER
isn't available to have stored the right host key in. But it also
permits you to specify a list of multiple host keys, which means a
second use case for the same mechanism will probably be round-robin
DNS names that select one of several servers with different host keys.
Host keys can be specified as the standard MD5 fingerprint or as an
SSH-2 base64 blob, and are canonicalised on input. (The base64 blob is
more unwieldy, especially with Windows command-line length limits, but
provides a means of specifying the _whole_ public key in case you
don't trust MD5. I haven't bothered to provide an analogous mechanism
for SSH-1, on the basis that anyone worrying about MD5 should have
stopped using SSH-1 already!)
[originally from svn r10220]
2014-09-09 11:46:24 +00:00
|
|
|
/*
|
|
|
|
* Construct the base64-encoded public key blob and see if
|
|
|
|
* that's listed.
|
|
|
|
*/
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf *binblob;
|
New option to manually configure the expected host key(s).
This option is available from the command line as '-hostkey', and is
also configurable through the GUI. When enabled, it completely
replaces all of the automated host key management: the server's host
key will be checked against the manually configured list, and the
connection will be allowed or disconnected on that basis, and the host
key store in the registry will not be either consulted or updated.
The main aim is to provide a means of automatically running Plink,
PSCP or PSFTP deep inside Windows services where HKEY_CURRENT_USER
isn't available to have stored the right host key in. But it also
permits you to specify a list of multiple host keys, which means a
second use case for the same mechanism will probably be round-robin
DNS names that select one of several servers with different host keys.
Host keys can be specified as the standard MD5 fingerprint or as an
SSH-2 base64 blob, and are canonicalised on input. (The base64 blob is
more unwieldy, especially with Windows command-line length limits, but
provides a means of specifying the _whole_ public key in case you
don't trust MD5. I haven't bothered to provide an analogous mechanism
for SSH-1, on the basis that anyone worrying about MD5 should have
stopped using SSH-1 already!)
[originally from svn r10220]
2014-09-09 11:46:24 +00:00
|
|
|
char *base64blob;
|
2018-05-24 09:59:39 +00:00
|
|
|
int atoms, i;
|
|
|
|
binblob = strbuf_new();
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh_key_public_blob(key, BinarySink_UPCAST(binblob));
|
2018-05-24 09:59:39 +00:00
|
|
|
atoms = (binblob->len + 2) / 3;
|
New option to manually configure the expected host key(s).
This option is available from the command line as '-hostkey', and is
also configurable through the GUI. When enabled, it completely
replaces all of the automated host key management: the server's host
key will be checked against the manually configured list, and the
connection will be allowed or disconnected on that basis, and the host
key store in the registry will not be either consulted or updated.
The main aim is to provide a means of automatically running Plink,
PSCP or PSFTP deep inside Windows services where HKEY_CURRENT_USER
isn't available to have stored the right host key in. But it also
permits you to specify a list of multiple host keys, which means a
second use case for the same mechanism will probably be round-robin
DNS names that select one of several servers with different host keys.
Host keys can be specified as the standard MD5 fingerprint or as an
SSH-2 base64 blob, and are canonicalised on input. (The base64 blob is
more unwieldy, especially with Windows command-line length limits, but
provides a means of specifying the _whole_ public key in case you
don't trust MD5. I haven't bothered to provide an analogous mechanism
for SSH-1, on the basis that anyone worrying about MD5 should have
stopped using SSH-1 already!)
[originally from svn r10220]
2014-09-09 11:46:24 +00:00
|
|
|
base64blob = snewn(atoms * 4 + 1, char);
|
|
|
|
for (i = 0; i < atoms; i++)
|
2018-05-24 09:59:39 +00:00
|
|
|
base64_encode_atom(binblob->u + 3*i,
|
|
|
|
binblob->len - 3*i, base64blob + 4*i);
|
New option to manually configure the expected host key(s).
This option is available from the command line as '-hostkey', and is
also configurable through the GUI. When enabled, it completely
replaces all of the automated host key management: the server's host
key will be checked against the manually configured list, and the
connection will be allowed or disconnected on that basis, and the host
key store in the registry will not be either consulted or updated.
The main aim is to provide a means of automatically running Plink,
PSCP or PSFTP deep inside Windows services where HKEY_CURRENT_USER
isn't available to have stored the right host key in. But it also
permits you to specify a list of multiple host keys, which means a
second use case for the same mechanism will probably be round-robin
DNS names that select one of several servers with different host keys.
Host keys can be specified as the standard MD5 fingerprint or as an
SSH-2 base64 blob, and are canonicalised on input. (The base64 blob is
more unwieldy, especially with Windows command-line length limits, but
provides a means of specifying the _whole_ public key in case you
don't trust MD5. I haven't bothered to provide an analogous mechanism
for SSH-1, on the basis that anyone worrying about MD5 should have
stopped using SSH-1 already!)
[originally from svn r10220]
2014-09-09 11:46:24 +00:00
|
|
|
base64blob[atoms * 4] = '\0';
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf_free(binblob);
|
New option to manually configure the expected host key(s).
This option is available from the command line as '-hostkey', and is
also configurable through the GUI. When enabled, it completely
replaces all of the automated host key management: the server's host
key will be checked against the manually configured list, and the
connection will be allowed or disconnected on that basis, and the host
key store in the registry will not be either consulted or updated.
The main aim is to provide a means of automatically running Plink,
PSCP or PSFTP deep inside Windows services where HKEY_CURRENT_USER
isn't available to have stored the right host key in. But it also
permits you to specify a list of multiple host keys, which means a
second use case for the same mechanism will probably be round-robin
DNS names that select one of several servers with different host keys.
Host keys can be specified as the standard MD5 fingerprint or as an
SSH-2 base64 blob, and are canonicalised on input. (The base64 blob is
more unwieldy, especially with Windows command-line length limits, but
provides a means of specifying the _whole_ public key in case you
don't trust MD5. I haven't bothered to provide an analogous mechanism
for SSH-1, on the basis that anyone worrying about MD5 should have
stopped using SSH-1 already!)
[originally from svn r10220]
2014-09-09 11:46:24 +00:00
|
|
|
if (conf_get_str_str_opt(ssh->conf, CONF_ssh_manual_hostkeys,
|
|
|
|
base64blob)) {
|
|
|
|
sfree(base64blob);
|
|
|
|
return 1; /* success */
|
|
|
|
}
|
|
|
|
sfree(base64blob);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh1_coro_wrapper_initial(Ssh ssh, PktIn *pktin);
|
|
|
|
static void ssh1_coro_wrapper_session(Ssh ssh, PktIn *pktin);
|
2018-05-18 06:22:58 +00:00
|
|
|
static void ssh1_connection_input(Ssh ssh);
|
|
|
|
|
2000-06-22 12:18:34 +00:00
|
|
|
/*
|
|
|
|
* Handle the key exchange and user authentication phases.
|
|
|
|
*/
|
2018-05-18 06:22:58 +00:00
|
|
|
static void do_ssh1_login(void *vctx)
|
2000-06-22 12:18:34 +00:00
|
|
|
{
|
2018-05-18 06:22:58 +00:00
|
|
|
Ssh ssh = (Ssh)vctx;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktIn *pktin;
|
2018-06-09 08:59:58 +00:00
|
|
|
PktOut *pkt;
|
2018-05-18 06:22:58 +00:00
|
|
|
|
2004-08-01 12:07:11 +00:00
|
|
|
int i, j, ret;
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen pl;
|
1999-01-08 13:02:13 +00:00
|
|
|
struct MD5Context md5c;
|
2002-10-25 11:30:33 +00:00
|
|
|
struct do_ssh1_login_state {
|
2012-08-18 09:30:01 +00:00
|
|
|
int crLine;
|
2002-10-25 11:30:33 +00:00
|
|
|
int len;
|
2015-05-05 19:16:17 +00:00
|
|
|
unsigned char *rsabuf;
|
2002-10-25 11:30:33 +00:00
|
|
|
unsigned long supported_ciphers_mask, supported_auths_mask;
|
|
|
|
int tried_publickey, tried_agent;
|
|
|
|
int tis_auth_refused, ccard_auth_refused;
|
2018-05-17 18:41:25 +00:00
|
|
|
unsigned char cookie[8];
|
2002-10-25 11:30:33 +00:00
|
|
|
unsigned char session_id[16];
|
|
|
|
int cipher_type;
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf *publickey_blob;
|
2005-10-30 20:24:09 +00:00
|
|
|
char *publickey_comment;
|
2015-05-12 11:30:25 +00:00
|
|
|
int privatekey_available, privatekey_encrypted;
|
2005-10-30 20:24:09 +00:00
|
|
|
prompts_t *cur_prompt;
|
2018-05-18 06:22:58 +00:00
|
|
|
int userpass_ret;
|
2002-10-25 11:30:33 +00:00
|
|
|
char c;
|
|
|
|
int pwpkt_type;
|
2018-05-27 22:58:20 +00:00
|
|
|
unsigned char *agent_response;
|
|
|
|
BinarySource asrc[1]; /* response from SSH agent */
|
2002-10-25 11:30:33 +00:00
|
|
|
int keyi, nkeys;
|
|
|
|
int authed;
|
|
|
|
struct RSAKey key;
|
|
|
|
Bignum challenge;
|
2018-05-27 22:58:20 +00:00
|
|
|
ptrlen comment;
|
2005-02-17 18:34:24 +00:00
|
|
|
int dlgret;
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
Filename *keyfile;
|
2013-07-07 14:34:37 +00:00
|
|
|
struct RSAKey servkey, hostkey;
|
2002-10-25 11:30:33 +00:00
|
|
|
};
|
|
|
|
crState(do_ssh1_login_state);
|
|
|
|
|
2012-08-18 09:30:01 +00:00
|
|
|
crBeginState;
|
1999-01-08 13:02:13 +00:00
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login)) != NULL);
|
1999-01-08 13:02:13 +00:00
|
|
|
|
2004-11-24 18:45:52 +00:00
|
|
|
if (pktin->type != SSH1_SMSG_PUBLIC_KEY) {
|
2003-03-31 11:22:30 +00:00
|
|
|
bombout(("Public key packet not received"));
|
2018-05-18 06:22:58 +00:00
|
|
|
crStopV;
|
2000-09-22 11:04:57 +00:00
|
|
|
}
|
1999-01-08 13:02:13 +00:00
|
|
|
|
1999-11-09 11:10:04 +00:00
|
|
|
logevent("Received public keys");
|
1999-01-08 13:02:13 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
pl = get_data(pktin, 8);
|
|
|
|
memcpy(s->cookie, pl.ptr, pl.len);
|
1999-01-08 13:02:13 +00:00
|
|
|
|
2018-06-03 07:23:07 +00:00
|
|
|
get_rsa_ssh1_pub(pktin, &s->servkey, RSA_SSH1_EXPONENT_FIRST);
|
|
|
|
get_rsa_ssh1_pub(pktin, &s->hostkey, RSA_SSH1_EXPONENT_FIRST);
|
1999-01-08 13:02:13 +00:00
|
|
|
|
2018-06-06 19:05:13 +00:00
|
|
|
s->hostkey.comment = NULL; /* avoid confusing rsa_ssh1_fingerprint */
|
|
|
|
|
1999-11-09 11:10:04 +00:00
|
|
|
/*
|
2000-09-26 14:26:21 +00:00
|
|
|
* Log the host key fingerprint.
|
1999-11-09 11:10:04 +00:00
|
|
|
*/
|
2018-05-27 17:13:53 +00:00
|
|
|
if (!get_err(pktin)) {
|
2018-06-03 07:08:53 +00:00
|
|
|
char *fingerprint = rsa_ssh1_fingerprint(&s->hostkey);
|
2000-09-26 14:26:21 +00:00
|
|
|
logevent("Host key fingerprint is:");
|
2018-06-03 07:08:53 +00:00
|
|
|
logeventf(ssh, " %s", fingerprint);
|
|
|
|
sfree(fingerprint);
|
1999-11-09 11:10:04 +00:00
|
|
|
}
|
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
ssh->v1_remote_protoflags = get_uint32(pktin);
|
|
|
|
s->supported_ciphers_mask = get_uint32(pktin);
|
|
|
|
s->supported_auths_mask = get_uint32(pktin);
|
|
|
|
|
|
|
|
if (get_err(pktin)) {
|
|
|
|
bombout(("Bad SSH-1 public key packet"));
|
|
|
|
crStopV;
|
|
|
|
}
|
|
|
|
|
2007-09-20 21:33:21 +00:00
|
|
|
if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA))
|
|
|
|
s->supported_auths_mask &= ~(1 << SSH1_AUTH_RSA);
|
1999-07-06 19:42:57 +00:00
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->v1_local_protoflags =
|
|
|
|
ssh->v1_remote_protoflags & SSH1_PROTOFLAGS_SUPPORTED;
|
|
|
|
ssh->v1_local_protoflags |= SSH1_PROTOFLAG_SCREEN_NUMBER;
|
2001-03-16 11:58:54 +00:00
|
|
|
|
1999-11-09 11:10:04 +00:00
|
|
|
MD5Init(&md5c);
|
2018-06-03 07:23:07 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = (bignum_bitcount(s->hostkey.modulus) + 7) / 8; i-- ;)
|
|
|
|
put_byte(&md5c, bignum_byte(s->hostkey.modulus, i));
|
|
|
|
for (i = (bignum_bitcount(s->servkey.modulus) + 7) / 8; i-- ;)
|
|
|
|
put_byte(&md5c, bignum_byte(s->servkey.modulus, i));
|
|
|
|
}
|
2018-05-24 09:03:36 +00:00
|
|
|
put_data(&md5c, s->cookie, 8);
|
2002-10-25 11:30:33 +00:00
|
|
|
MD5Final(s->session_id, &md5c);
|
1999-01-08 13:02:13 +00:00
|
|
|
|
2001-05-06 14:35:20 +00:00
|
|
|
for (i = 0; i < 32; i++)
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->session_key[i] = random_byte();
|
1999-01-08 13:02:13 +00:00
|
|
|
|
2004-08-01 12:07:11 +00:00
|
|
|
/*
|
|
|
|
* Verify that the `bits' and `bytes' parameters match.
|
|
|
|
*/
|
2013-07-07 14:34:37 +00:00
|
|
|
if (s->hostkey.bits > s->hostkey.bytes * 8 ||
|
|
|
|
s->servkey.bits > s->servkey.bytes * 8) {
|
2005-03-10 16:36:05 +00:00
|
|
|
bombout(("SSH-1 public keys were badly formatted"));
|
2018-05-18 06:22:58 +00:00
|
|
|
crStopV;
|
2004-08-01 12:07:11 +00:00
|
|
|
}
|
|
|
|
|
2013-07-07 14:34:37 +00:00
|
|
|
s->len = (s->hostkey.bytes > s->servkey.bytes ?
|
|
|
|
s->hostkey.bytes : s->servkey.bytes);
|
1999-01-08 13:02:13 +00:00
|
|
|
|
2003-03-29 16:14:26 +00:00
|
|
|
s->rsabuf = snewn(s->len, unsigned char);
|
1999-01-08 13:02:13 +00:00
|
|
|
|
2000-03-15 15:08:48 +00:00
|
|
|
/*
|
|
|
|
* Verify the host key.
|
|
|
|
*/
|
|
|
|
{
|
2001-05-06 14:35:20 +00:00
|
|
|
/*
|
|
|
|
* First format the key into a string.
|
|
|
|
*/
|
2013-07-07 14:34:37 +00:00
|
|
|
int len = rsastr_len(&s->hostkey);
|
2018-06-03 07:08:53 +00:00
|
|
|
char *fingerprint;
|
2003-03-29 16:14:26 +00:00
|
|
|
char *keystr = snewn(len, char);
|
2013-07-07 14:34:37 +00:00
|
|
|
rsastr_fmt(keystr, &s->hostkey);
|
2018-06-03 07:08:53 +00:00
|
|
|
fingerprint = rsa_ssh1_fingerprint(&s->hostkey);
|
2005-02-17 18:34:24 +00:00
|
|
|
|
New option to manually configure the expected host key(s).
This option is available from the command line as '-hostkey', and is
also configurable through the GUI. When enabled, it completely
replaces all of the automated host key management: the server's host
key will be checked against the manually configured list, and the
connection will be allowed or disconnected on that basis, and the host
key store in the registry will not be either consulted or updated.
The main aim is to provide a means of automatically running Plink,
PSCP or PSFTP deep inside Windows services where HKEY_CURRENT_USER
isn't available to have stored the right host key in. But it also
permits you to specify a list of multiple host keys, which means a
second use case for the same mechanism will probably be round-robin
DNS names that select one of several servers with different host keys.
Host keys can be specified as the standard MD5 fingerprint or as an
SSH-2 base64 blob, and are canonicalised on input. (The base64 blob is
more unwieldy, especially with Windows command-line length limits, but
provides a means of specifying the _whole_ public key in case you
don't trust MD5. I haven't bothered to provide an analogous mechanism
for SSH-1, on the basis that anyone worrying about MD5 should have
stopped using SSH-1 already!)
[originally from svn r10220]
2014-09-09 11:46:24 +00:00
|
|
|
/* First check against manually configured host keys. */
|
2018-06-03 11:58:05 +00:00
|
|
|
s->dlgret = verify_ssh_manual_host_key(ssh, fingerprint, NULL);
|
2018-06-03 07:08:53 +00:00
|
|
|
sfree(fingerprint);
|
New option to manually configure the expected host key(s).
This option is available from the command line as '-hostkey', and is
also configurable through the GUI. When enabled, it completely
replaces all of the automated host key management: the server's host
key will be checked against the manually configured list, and the
connection will be allowed or disconnected on that basis, and the host
key store in the registry will not be either consulted or updated.
The main aim is to provide a means of automatically running Plink,
PSCP or PSFTP deep inside Windows services where HKEY_CURRENT_USER
isn't available to have stored the right host key in. But it also
permits you to specify a list of multiple host keys, which means a
second use case for the same mechanism will probably be round-robin
DNS names that select one of several servers with different host keys.
Host keys can be specified as the standard MD5 fingerprint or as an
SSH-2 base64 blob, and are canonicalised on input. (The base64 blob is
more unwieldy, especially with Windows command-line length limits, but
provides a means of specifying the _whole_ public key in case you
don't trust MD5. I haven't bothered to provide an analogous mechanism
for SSH-1, on the basis that anyone worrying about MD5 should have
stopped using SSH-1 already!)
[originally from svn r10220]
2014-09-09 11:46:24 +00:00
|
|
|
if (s->dlgret == 0) { /* did not match */
|
|
|
|
bombout(("Host key did not appear in manually configured list"));
|
2014-11-22 09:58:15 +00:00
|
|
|
sfree(keystr);
|
2018-05-18 06:22:58 +00:00
|
|
|
crStopV;
|
New option to manually configure the expected host key(s).
This option is available from the command line as '-hostkey', and is
also configurable through the GUI. When enabled, it completely
replaces all of the automated host key management: the server's host
key will be checked against the manually configured list, and the
connection will be allowed or disconnected on that basis, and the host
key store in the registry will not be either consulted or updated.
The main aim is to provide a means of automatically running Plink,
PSCP or PSFTP deep inside Windows services where HKEY_CURRENT_USER
isn't available to have stored the right host key in. But it also
permits you to specify a list of multiple host keys, which means a
second use case for the same mechanism will probably be round-robin
DNS names that select one of several servers with different host keys.
Host keys can be specified as the standard MD5 fingerprint or as an
SSH-2 base64 blob, and are canonicalised on input. (The base64 blob is
more unwieldy, especially with Windows command-line length limits, but
provides a means of specifying the _whole_ public key in case you
don't trust MD5. I haven't bothered to provide an analogous mechanism
for SSH-1, on the basis that anyone worrying about MD5 should have
stopped using SSH-1 already!)
[originally from svn r10220]
2014-09-09 11:46:24 +00:00
|
|
|
} else if (s->dlgret < 0) { /* none configured; use standard handling */
|
|
|
|
s->dlgret = verify_ssh_host_key(ssh->frontend,
|
|
|
|
ssh->savedhost, ssh->savedport,
|
|
|
|
"rsa", keystr, fingerprint,
|
|
|
|
ssh_dialog_callback, ssh);
|
|
|
|
sfree(keystr);
|
2015-10-18 12:04:58 +00:00
|
|
|
#ifdef FUZZING
|
|
|
|
s->dlgret = 1;
|
|
|
|
#endif
|
New option to manually configure the expected host key(s).
This option is available from the command line as '-hostkey', and is
also configurable through the GUI. When enabled, it completely
replaces all of the automated host key management: the server's host
key will be checked against the manually configured list, and the
connection will be allowed or disconnected on that basis, and the host
key store in the registry will not be either consulted or updated.
The main aim is to provide a means of automatically running Plink,
PSCP or PSFTP deep inside Windows services where HKEY_CURRENT_USER
isn't available to have stored the right host key in. But it also
permits you to specify a list of multiple host keys, which means a
second use case for the same mechanism will probably be round-robin
DNS names that select one of several servers with different host keys.
Host keys can be specified as the standard MD5 fingerprint or as an
SSH-2 base64 blob, and are canonicalised on input. (The base64 blob is
more unwieldy, especially with Windows command-line length limits, but
provides a means of specifying the _whole_ public key in case you
don't trust MD5. I haven't bothered to provide an analogous mechanism
for SSH-1, on the basis that anyone worrying about MD5 should have
stopped using SSH-1 already!)
[originally from svn r10220]
2014-09-09 11:46:24 +00:00
|
|
|
if (s->dlgret < 0) {
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->user_response = -1;
|
2018-05-18 06:22:58 +00:00
|
|
|
crWaitUntilV(ssh->user_response >= 0);
|
New option to manually configure the expected host key(s).
This option is available from the command line as '-hostkey', and is
also configurable through the GUI. When enabled, it completely
replaces all of the automated host key management: the server's host
key will be checked against the manually configured list, and the
connection will be allowed or disconnected on that basis, and the host
key store in the registry will not be either consulted or updated.
The main aim is to provide a means of automatically running Plink,
PSCP or PSFTP deep inside Windows services where HKEY_CURRENT_USER
isn't available to have stored the right host key in. But it also
permits you to specify a list of multiple host keys, which means a
second use case for the same mechanism will probably be round-robin
DNS names that select one of several servers with different host keys.
Host keys can be specified as the standard MD5 fingerprint or as an
SSH-2 base64 blob, and are canonicalised on input. (The base64 blob is
more unwieldy, especially with Windows command-line length limits, but
provides a means of specifying the _whole_ public key in case you
don't trust MD5. I haven't bothered to provide an analogous mechanism
for SSH-1, on the basis that anyone worrying about MD5 should have
stopped using SSH-1 already!)
[originally from svn r10220]
2014-09-09 11:46:24 +00:00
|
|
|
s->dlgret = ssh->user_response;
|
|
|
|
}
|
2005-02-17 18:34:24 +00:00
|
|
|
|
New option to manually configure the expected host key(s).
This option is available from the command line as '-hostkey', and is
also configurable through the GUI. When enabled, it completely
replaces all of the automated host key management: the server's host
key will be checked against the manually configured list, and the
connection will be allowed or disconnected on that basis, and the host
key store in the registry will not be either consulted or updated.
The main aim is to provide a means of automatically running Plink,
PSCP or PSFTP deep inside Windows services where HKEY_CURRENT_USER
isn't available to have stored the right host key in. But it also
permits you to specify a list of multiple host keys, which means a
second use case for the same mechanism will probably be round-robin
DNS names that select one of several servers with different host keys.
Host keys can be specified as the standard MD5 fingerprint or as an
SSH-2 base64 blob, and are canonicalised on input. (The base64 blob is
more unwieldy, especially with Windows command-line length limits, but
provides a means of specifying the _whole_ public key in case you
don't trust MD5. I haven't bothered to provide an analogous mechanism
for SSH-1, on the basis that anyone worrying about MD5 should have
stopped using SSH-1 already!)
[originally from svn r10220]
2014-09-09 11:46:24 +00:00
|
|
|
if (s->dlgret == 0) {
|
|
|
|
ssh_disconnect(ssh, "User aborted at host key verification",
|
|
|
|
NULL, 0, TRUE);
|
2018-05-18 06:22:58 +00:00
|
|
|
crStopV;
|
New option to manually configure the expected host key(s).
This option is available from the command line as '-hostkey', and is
also configurable through the GUI. When enabled, it completely
replaces all of the automated host key management: the server's host
key will be checked against the manually configured list, and the
connection will be allowed or disconnected on that basis, and the host
key store in the registry will not be either consulted or updated.
The main aim is to provide a means of automatically running Plink,
PSCP or PSFTP deep inside Windows services where HKEY_CURRENT_USER
isn't available to have stored the right host key in. But it also
permits you to specify a list of multiple host keys, which means a
second use case for the same mechanism will probably be round-robin
DNS names that select one of several servers with different host keys.
Host keys can be specified as the standard MD5 fingerprint or as an
SSH-2 base64 blob, and are canonicalised on input. (The base64 blob is
more unwieldy, especially with Windows command-line length limits, but
provides a means of specifying the _whole_ public key in case you
don't trust MD5. I haven't bothered to provide an analogous mechanism
for SSH-1, on the basis that anyone worrying about MD5 should have
stopped using SSH-1 already!)
[originally from svn r10220]
2014-09-09 11:46:24 +00:00
|
|
|
}
|
2014-11-22 09:58:15 +00:00
|
|
|
} else {
|
|
|
|
sfree(keystr);
|
2005-02-17 18:34:24 +00:00
|
|
|
}
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 32; i++) {
|
2002-10-25 11:30:33 +00:00
|
|
|
s->rsabuf[i] = ssh->session_key[i];
|
1999-01-08 13:02:13 +00:00
|
|
|
if (i < 16)
|
2002-10-25 11:30:33 +00:00
|
|
|
s->rsabuf[i] ^= s->session_id[i];
|
1999-01-08 13:02:13 +00:00
|
|
|
}
|
|
|
|
|
2013-07-07 14:34:37 +00:00
|
|
|
if (s->hostkey.bytes > s->servkey.bytes) {
|
2018-05-24 07:22:44 +00:00
|
|
|
ret = rsa_ssh1_encrypt(s->rsabuf, 32, &s->servkey);
|
2004-08-01 12:07:11 +00:00
|
|
|
if (ret)
|
2018-05-24 07:22:44 +00:00
|
|
|
ret = rsa_ssh1_encrypt(s->rsabuf, s->servkey.bytes, &s->hostkey);
|
1999-01-08 13:02:13 +00:00
|
|
|
} else {
|
2018-05-24 07:22:44 +00:00
|
|
|
ret = rsa_ssh1_encrypt(s->rsabuf, 32, &s->hostkey);
|
2004-08-01 12:07:11 +00:00
|
|
|
if (ret)
|
2018-05-24 07:22:44 +00:00
|
|
|
ret = rsa_ssh1_encrypt(s->rsabuf, s->hostkey.bytes, &s->servkey);
|
2004-08-01 12:07:11 +00:00
|
|
|
}
|
|
|
|
if (!ret) {
|
2005-03-10 16:36:05 +00:00
|
|
|
bombout(("SSH-1 public key encryptions failed due to bad formatting"));
|
2018-05-18 06:22:58 +00:00
|
|
|
crStopV;
|
1999-01-08 13:02:13 +00:00
|
|
|
}
|
|
|
|
|
1999-11-09 11:10:04 +00:00
|
|
|
logevent("Encrypted session key");
|
|
|
|
|
2001-08-25 19:33:33 +00:00
|
|
|
{
|
|
|
|
int cipher_chosen = 0, warn = 0;
|
2015-05-15 10:15:42 +00:00
|
|
|
const char *cipher_string = NULL;
|
2002-10-25 11:30:33 +00:00
|
|
|
int i;
|
2001-08-25 19:33:33 +00:00
|
|
|
for (i = 0; !cipher_chosen && i < CIPHER_MAX; i++) {
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
int next_cipher = conf_get_int_int(ssh->conf,
|
|
|
|
CONF_ssh_cipherlist, i);
|
2001-08-25 19:33:33 +00:00
|
|
|
if (next_cipher == CIPHER_WARN) {
|
|
|
|
/* If/when we choose a cipher, warn about it */
|
|
|
|
warn = 1;
|
|
|
|
} else if (next_cipher == CIPHER_AES) {
|
|
|
|
/* XXX Probably don't need to mention this. */
|
2005-03-10 16:36:05 +00:00
|
|
|
logevent("AES not supported in SSH-1, skipping");
|
2001-08-25 19:33:33 +00:00
|
|
|
} else {
|
|
|
|
switch (next_cipher) {
|
2002-10-25 11:30:33 +00:00
|
|
|
case CIPHER_3DES: s->cipher_type = SSH_CIPHER_3DES;
|
2001-08-25 19:33:33 +00:00
|
|
|
cipher_string = "3DES"; break;
|
2002-10-25 11:30:33 +00:00
|
|
|
case CIPHER_BLOWFISH: s->cipher_type = SSH_CIPHER_BLOWFISH;
|
2001-08-25 19:33:33 +00:00
|
|
|
cipher_string = "Blowfish"; break;
|
2002-10-25 11:30:33 +00:00
|
|
|
case CIPHER_DES: s->cipher_type = SSH_CIPHER_DES;
|
2001-08-25 19:33:33 +00:00
|
|
|
cipher_string = "single-DES"; break;
|
|
|
|
}
|
2002-10-25 11:30:33 +00:00
|
|
|
if (s->supported_ciphers_mask & (1 << s->cipher_type))
|
2001-08-25 19:33:33 +00:00
|
|
|
cipher_chosen = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!cipher_chosen) {
|
2002-10-25 11:30:33 +00:00
|
|
|
if ((s->supported_ciphers_mask & (1 << SSH_CIPHER_3DES)) == 0)
|
2005-03-10 16:36:05 +00:00
|
|
|
bombout(("Server violates SSH-1 protocol by not "
|
2001-08-25 19:33:33 +00:00
|
|
|
"supporting 3DES encryption"));
|
|
|
|
else
|
|
|
|
/* shouldn't happen */
|
2003-03-31 11:22:30 +00:00
|
|
|
bombout(("No supported ciphers found"));
|
2018-05-18 06:22:58 +00:00
|
|
|
crStopV;
|
2001-04-16 07:49:11 +00:00
|
|
|
}
|
2001-08-25 19:33:33 +00:00
|
|
|
|
|
|
|
/* Warn about chosen cipher if necessary. */
|
2005-01-07 09:24:24 +00:00
|
|
|
if (warn) {
|
2005-02-17 18:34:24 +00:00
|
|
|
s->dlgret = askalg(ssh->frontend, "cipher", cipher_string,
|
|
|
|
ssh_dialog_callback, ssh);
|
|
|
|
if (s->dlgret < 0) {
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->user_response = -1;
|
2018-05-18 06:22:58 +00:00
|
|
|
crWaitUntilV(ssh->user_response >= 0);
|
2005-02-17 18:34:24 +00:00
|
|
|
s->dlgret = ssh->user_response;
|
|
|
|
}
|
|
|
|
if (s->dlgret == 0) {
|
2005-05-21 16:49:27 +00:00
|
|
|
ssh_disconnect(ssh, "User aborted at cipher warning", NULL,
|
|
|
|
0, TRUE);
|
2018-05-18 06:22:58 +00:00
|
|
|
crStopV;
|
2005-02-17 18:34:24 +00:00
|
|
|
}
|
2005-01-07 09:24:24 +00:00
|
|
|
}
|
1999-07-06 19:42:57 +00:00
|
|
|
}
|
2001-08-25 19:33:33 +00:00
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
switch (s->cipher_type) {
|
2001-05-06 14:35:20 +00:00
|
|
|
case SSH_CIPHER_3DES:
|
|
|
|
logevent("Using 3DES encryption");
|
|
|
|
break;
|
|
|
|
case SSH_CIPHER_DES:
|
|
|
|
logevent("Using single-DES encryption");
|
|
|
|
break;
|
|
|
|
case SSH_CIPHER_BLOWFISH:
|
|
|
|
logevent("Using Blowfish encryption");
|
|
|
|
break;
|
1999-11-09 11:10:04 +00:00
|
|
|
}
|
1999-07-06 19:42:57 +00:00
|
|
|
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_SESSION_KEY);
|
|
|
|
put_byte(pkt, s->cipher_type);
|
|
|
|
put_data(pkt, s->cookie, 8);
|
|
|
|
put_uint16(pkt, s->len * 8);
|
|
|
|
put_data(pkt, s->rsabuf, s->len);
|
|
|
|
put_uint32(pkt, ssh->v1_local_protoflags);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2000-06-22 12:18:34 +00:00
|
|
|
|
1999-11-09 11:10:04 +00:00
|
|
|
logevent("Trying to enable encryption...");
|
1999-01-08 13:02:13 +00:00
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
sfree(s->rsabuf);
|
1999-01-08 13:02:13 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
{
|
2018-09-13 12:29:32 +00:00
|
|
|
const struct ssh1_cipheralg *cipher =
|
|
|
|
(s->cipher_type == SSH_CIPHER_BLOWFISH ? &ssh1_blowfish :
|
|
|
|
s->cipher_type == SSH_CIPHER_DES ? &ssh1_des : &ssh1_3des);
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh1_bpp_new_cipher(ssh->bpp, cipher, ssh->session_key);
|
|
|
|
logeventf(ssh, "Initialised %s encryption", cipher->text_name);
|
|
|
|
}
|
2002-10-25 12:58:21 +00:00
|
|
|
|
2013-07-07 14:34:37 +00:00
|
|
|
if (s->servkey.modulus) {
|
|
|
|
sfree(s->servkey.modulus);
|
|
|
|
s->servkey.modulus = NULL;
|
2003-12-19 12:44:46 +00:00
|
|
|
}
|
2013-07-07 14:34:37 +00:00
|
|
|
if (s->servkey.exponent) {
|
|
|
|
sfree(s->servkey.exponent);
|
|
|
|
s->servkey.exponent = NULL;
|
2003-12-19 12:44:46 +00:00
|
|
|
}
|
2013-07-07 14:34:37 +00:00
|
|
|
if (s->hostkey.modulus) {
|
|
|
|
sfree(s->hostkey.modulus);
|
|
|
|
s->hostkey.modulus = NULL;
|
2003-12-19 12:44:46 +00:00
|
|
|
}
|
2013-07-07 14:34:37 +00:00
|
|
|
if (s->hostkey.exponent) {
|
|
|
|
sfree(s->hostkey.exponent);
|
|
|
|
s->hostkey.exponent = NULL;
|
2003-12-19 12:44:46 +00:00
|
|
|
}
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login)) != NULL);
|
1999-01-08 13:02:13 +00:00
|
|
|
|
2004-11-24 18:45:52 +00:00
|
|
|
if (pktin->type != SSH1_SMSG_SUCCESS) {
|
2003-03-31 11:22:30 +00:00
|
|
|
bombout(("Encryption not successfully enabled"));
|
2018-05-18 06:22:58 +00:00
|
|
|
crStopV;
|
2000-09-22 11:04:57 +00:00
|
|
|
}
|
1999-01-08 13:02:13 +00:00
|
|
|
|
1999-11-09 11:10:04 +00:00
|
|
|
logevent("Successfully started encryption");
|
|
|
|
|
2005-10-30 20:24:09 +00:00
|
|
|
fflush(stdout); /* FIXME eh? */
|
2018-05-18 06:22:58 +00:00
|
|
|
if ((ssh->username = get_remote_username(ssh->conf)) == NULL) {
|
|
|
|
s->cur_prompt = new_prompts(ssh->frontend);
|
|
|
|
s->cur_prompt->to_server = TRUE;
|
|
|
|
s->cur_prompt->name = dupstr("SSH login name");
|
|
|
|
add_prompt(s->cur_prompt, dupstr("login as: "), TRUE);
|
|
|
|
s->userpass_ret = get_userpass_input(s->cur_prompt, NULL);
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
while (1) {
|
2018-05-18 06:22:58 +00:00
|
|
|
while (s->userpass_ret < 0 &&
|
|
|
|
bufchain_size(&ssh->user_input) > 0)
|
|
|
|
s->userpass_ret = get_userpass_input(
|
|
|
|
s->cur_prompt, &ssh->user_input);
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
|
|
|
|
if (s->userpass_ret >= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ssh->send_ok = 1;
|
|
|
|
crReturnV;
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->send_ok = 0;
|
|
|
|
}
|
|
|
|
if (!s->userpass_ret) {
|
|
|
|
/*
|
|
|
|
* Failed to get a username. Terminate.
|
|
|
|
*/
|
|
|
|
free_prompts(s->cur_prompt);
|
|
|
|
ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE);
|
|
|
|
crStopV;
|
|
|
|
}
|
|
|
|
ssh->username = dupstr(s->cur_prompt->prompts[0]->result);
|
|
|
|
free_prompts(s->cur_prompt);
|
|
|
|
}
|
2000-06-22 12:18:34 +00:00
|
|
|
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_USER);
|
|
|
|
put_stringz(pkt, ssh->username);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
{
|
|
|
|
char *userlog = dupprintf("Sent username \"%s\"", ssh->username);
|
|
|
|
logevent(userlog);
|
|
|
|
if (flags & FLAG_INTERACTIVE &&
|
|
|
|
(!((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)))) {
|
|
|
|
c_write_str(ssh, userlog);
|
|
|
|
c_write_str(ssh, "\r\n");
|
|
|
|
}
|
|
|
|
sfree(userlog);
|
1999-01-08 13:02:13 +00:00
|
|
|
}
|
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login)) != NULL);
|
1999-01-08 13:02:13 +00:00
|
|
|
|
2007-09-20 21:33:21 +00:00
|
|
|
if ((s->supported_auths_mask & (1 << SSH1_AUTH_RSA)) == 0) {
|
2002-02-27 22:41:31 +00:00
|
|
|
/* We must not attempt PK auth. Pretend we've already tried it. */
|
2002-10-25 11:30:33 +00:00
|
|
|
s->tried_publickey = s->tried_agent = 1;
|
2002-02-27 22:41:31 +00:00
|
|
|
} else {
|
2002-10-25 11:30:33 +00:00
|
|
|
s->tried_publickey = s->tried_agent = 0;
|
2002-02-27 22:41:31 +00:00
|
|
|
}
|
2002-10-25 11:30:33 +00:00
|
|
|
s->tis_auth_refused = s->ccard_auth_refused = 0;
|
2005-10-30 20:24:09 +00:00
|
|
|
/*
|
|
|
|
* Load the public half of any configured keyfile for later use.
|
|
|
|
*/
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
|
2011-10-02 11:01:57 +00:00
|
|
|
if (!filename_is_null(s->keyfile)) {
|
2005-10-30 20:24:09 +00:00
|
|
|
int keytype;
|
2015-05-12 11:30:25 +00:00
|
|
|
logeventf(ssh, "Reading key file \"%.150s\"",
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
filename_to_str(s->keyfile));
|
|
|
|
keytype = key_type(s->keyfile);
|
2015-05-12 11:30:25 +00:00
|
|
|
if (keytype == SSH_KEYTYPE_SSH1 ||
|
|
|
|
keytype == SSH_KEYTYPE_SSH1_PUBLIC) {
|
2005-10-30 20:24:09 +00:00
|
|
|
const char *error;
|
2018-05-24 09:59:39 +00:00
|
|
|
s->publickey_blob = strbuf_new();
|
2018-05-24 07:22:44 +00:00
|
|
|
if (rsa_ssh1_loadpub(s->keyfile,
|
2018-05-24 09:59:39 +00:00
|
|
|
BinarySink_UPCAST(s->publickey_blob),
|
2018-05-24 07:22:44 +00:00
|
|
|
&s->publickey_comment, &error)) {
|
2015-05-12 11:30:25 +00:00
|
|
|
s->privatekey_available = (keytype == SSH_KEYTYPE_SSH1);
|
|
|
|
if (!s->privatekey_available)
|
|
|
|
logeventf(ssh, "Key file contains public key only");
|
2018-05-24 07:22:44 +00:00
|
|
|
s->privatekey_encrypted = rsa_ssh1_encrypted(s->keyfile, NULL);
|
2005-10-30 20:24:09 +00:00
|
|
|
} else {
|
|
|
|
char *msgbuf;
|
2015-05-12 11:30:25 +00:00
|
|
|
logeventf(ssh, "Unable to load key (%s)", error);
|
|
|
|
msgbuf = dupprintf("Unable to load key file "
|
2005-10-30 20:24:09 +00:00
|
|
|
"\"%.150s\" (%s)\r\n",
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
filename_to_str(s->keyfile),
|
2005-10-30 20:24:09 +00:00
|
|
|
error);
|
|
|
|
c_write_str(ssh, msgbuf);
|
|
|
|
sfree(msgbuf);
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf_free(s->publickey_blob);
|
|
|
|
s->publickey_blob = NULL;
|
2005-10-30 20:24:09 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
char *msgbuf;
|
|
|
|
logeventf(ssh, "Unable to use this key file (%s)",
|
|
|
|
key_type_to_str(keytype));
|
|
|
|
msgbuf = dupprintf("Unable to use key file \"%.150s\""
|
|
|
|
" (%s)\r\n",
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
filename_to_str(s->keyfile),
|
2005-10-30 20:24:09 +00:00
|
|
|
key_type_to_str(keytype));
|
|
|
|
c_write_str(ssh, msgbuf);
|
|
|
|
sfree(msgbuf);
|
2002-10-25 11:30:33 +00:00
|
|
|
s->publickey_blob = NULL;
|
2005-10-30 20:24:09 +00:00
|
|
|
}
|
2001-12-30 16:20:31 +00:00
|
|
|
} else
|
2002-10-25 11:30:33 +00:00
|
|
|
s->publickey_blob = NULL;
|
2000-09-07 16:33:49 +00:00
|
|
|
|
2004-11-24 18:45:52 +00:00
|
|
|
while (pktin->type == SSH1_SMSG_FAILURE) {
|
2002-10-25 11:30:33 +00:00
|
|
|
s->pwpkt_type = SSH1_CMSG_AUTH_PASSWORD;
|
2001-09-08 12:50:15 +00:00
|
|
|
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (conf_get_int(ssh->conf, CONF_tryagent) && agent_exists() && !s->tried_agent) {
|
2001-05-06 14:35:20 +00:00
|
|
|
/*
|
|
|
|
* Attempt RSA authentication using Pageant.
|
|
|
|
*/
|
|
|
|
void *r;
|
2018-05-27 22:58:20 +00:00
|
|
|
int rlen;
|
2018-05-24 12:18:13 +00:00
|
|
|
strbuf *request;
|
2001-05-06 14:35:20 +00:00
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
s->authed = FALSE;
|
|
|
|
s->tried_agent = 1;
|
2001-05-06 14:35:20 +00:00
|
|
|
logevent("Pageant is running. Requesting keys.");
|
|
|
|
|
|
|
|
/* Request the keys held by the agent. */
|
2018-05-24 12:18:13 +00:00
|
|
|
request = strbuf_new_for_agent_query();
|
|
|
|
put_byte(request, SSH1_AGENTC_REQUEST_RSA_IDENTITIES);
|
2017-01-29 20:24:15 +00:00
|
|
|
ssh->auth_agent_query = agent_query(
|
2018-05-27 22:58:20 +00:00
|
|
|
request, &r, &rlen, ssh_agent_callback, ssh);
|
2018-05-24 12:18:13 +00:00
|
|
|
strbuf_free(request);
|
2017-01-29 20:24:15 +00:00
|
|
|
if (ssh->auth_agent_query) {
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->agent_response = NULL;
|
2018-05-18 06:22:58 +00:00
|
|
|
crWaitUntilV(ssh->agent_response);
|
2003-04-28 11:41:39 +00:00
|
|
|
r = ssh->agent_response;
|
2018-05-27 22:58:20 +00:00
|
|
|
rlen = ssh->agent_response_len;
|
2003-04-28 11:41:39 +00:00
|
|
|
}
|
2018-05-27 22:58:20 +00:00
|
|
|
s->agent_response = r;
|
|
|
|
BinarySource_BARE_INIT(s->asrc, r, rlen);
|
|
|
|
get_uint32(s->asrc); /* skip length field */
|
|
|
|
if (get_byte(s->asrc) == SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
|
|
|
|
s->nkeys = toint(get_uint32(s->asrc));
|
2013-07-14 10:45:54 +00:00
|
|
|
if (s->nkeys < 0) {
|
|
|
|
logeventf(ssh, "Pageant reported negative key count %d",
|
|
|
|
s->nkeys);
|
|
|
|
s->nkeys = 0;
|
|
|
|
}
|
2005-03-10 16:36:05 +00:00
|
|
|
logeventf(ssh, "Pageant has %d SSH-1 keys", s->nkeys);
|
2002-10-25 11:30:33 +00:00
|
|
|
for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) {
|
2018-06-03 07:23:07 +00:00
|
|
|
size_t start, end;
|
|
|
|
start = s->asrc->pos;
|
|
|
|
get_rsa_ssh1_pub(s->asrc, &s->key,
|
2018-05-27 22:58:20 +00:00
|
|
|
RSA_SSH1_EXPONENT_FIRST);
|
2018-06-03 07:23:07 +00:00
|
|
|
end = s->asrc->pos;
|
2018-05-27 22:58:20 +00:00
|
|
|
s->comment = get_string(s->asrc);
|
|
|
|
if (get_err(s->asrc)) {
|
|
|
|
logevent("Pageant key list packet was truncated");
|
|
|
|
break;
|
|
|
|
}
|
2005-11-13 16:06:41 +00:00
|
|
|
if (s->publickey_blob) {
|
2018-06-03 07:23:07 +00:00
|
|
|
ptrlen keystr = make_ptrlen(
|
|
|
|
(const char *)s->asrc->data + start, end - start);
|
|
|
|
|
2018-05-27 22:58:20 +00:00
|
|
|
if (keystr.len == s->publickey_blob->len &&
|
|
|
|
!memcmp(keystr.ptr, s->publickey_blob->s,
|
2018-05-24 09:59:39 +00:00
|
|
|
s->publickey_blob->len)) {
|
2005-11-13 16:06:41 +00:00
|
|
|
logeventf(ssh, "Pageant key #%d matches "
|
|
|
|
"configured key file", s->keyi);
|
|
|
|
s->tried_publickey = 1;
|
|
|
|
} else
|
|
|
|
/* Skip non-configured key */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
logeventf(ssh, "Trying Pageant key #%d", s->keyi);
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_AUTH_RSA);
|
|
|
|
put_mp_ssh1(pkt, s->key.modulus);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login))
|
|
|
|
!= NULL);
|
2004-11-24 18:45:52 +00:00
|
|
|
if (pktin->type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {
|
2001-05-06 14:35:20 +00:00
|
|
|
logevent("Key refused");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
logevent("Received RSA challenge");
|
2018-05-27 17:13:53 +00:00
|
|
|
s->challenge = get_mp_ssh1(pktin);
|
|
|
|
if (get_err(pktin)) {
|
|
|
|
freebn(s->challenge);
|
2004-08-01 12:07:11 +00:00
|
|
|
bombout(("Server's RSA challenge was badly formatted"));
|
2018-05-18 06:22:58 +00:00
|
|
|
crStopV;
|
2004-08-01 12:07:11 +00:00
|
|
|
}
|
|
|
|
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2018-05-24 12:18:13 +00:00
|
|
|
strbuf *agentreq;
|
|
|
|
char *ret;
|
2001-05-13 14:02:28 +00:00
|
|
|
void *vret;
|
2018-05-24 12:18:13 +00:00
|
|
|
int retlen;
|
|
|
|
|
|
|
|
agentreq = strbuf_new_for_agent_query();
|
|
|
|
put_byte(agentreq, SSH1_AGENTC_RSA_CHALLENGE);
|
|
|
|
put_uint32(agentreq, bignum_bitcount(s->key.modulus));
|
|
|
|
put_mp_ssh1(agentreq, s->key.exponent);
|
|
|
|
put_mp_ssh1(agentreq, s->key.modulus);
|
|
|
|
put_mp_ssh1(agentreq, s->challenge);
|
|
|
|
put_data(agentreq, s->session_id, 16);
|
|
|
|
put_uint32(agentreq, 1); /* response format */
|
2017-01-29 20:24:15 +00:00
|
|
|
ssh->auth_agent_query = agent_query(
|
2018-05-24 12:18:13 +00:00
|
|
|
agentreq, &vret, &retlen,
|
2017-01-29 20:24:15 +00:00
|
|
|
ssh_agent_callback, ssh);
|
2018-05-24 12:18:13 +00:00
|
|
|
strbuf_free(agentreq);
|
|
|
|
|
2017-01-29 20:24:15 +00:00
|
|
|
if (ssh->auth_agent_query) {
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->agent_response = NULL;
|
2018-05-18 06:22:58 +00:00
|
|
|
crWaitUntilV(ssh->agent_response);
|
2003-04-28 11:41:39 +00:00
|
|
|
vret = ssh->agent_response;
|
|
|
|
retlen = ssh->agent_response_len;
|
2018-05-24 12:18:13 +00:00
|
|
|
}
|
|
|
|
|
2001-05-13 14:02:28 +00:00
|
|
|
ret = vret;
|
2001-05-06 14:35:20 +00:00
|
|
|
if (ret) {
|
|
|
|
if (ret[4] == SSH1_AGENT_RSA_RESPONSE) {
|
|
|
|
logevent("Sending Pageant's response");
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp,
|
|
|
|
SSH1_CMSG_AUTH_RSA_RESPONSE);
|
|
|
|
put_data(pkt, ret + 5, 16);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2001-05-06 14:35:20 +00:00
|
|
|
sfree(ret);
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV(
|
|
|
|
(pktin = pq_pop(&ssh->pq_ssh1_login))
|
|
|
|
!= NULL);
|
2004-11-24 18:45:52 +00:00
|
|
|
if (pktin->type == SSH1_SMSG_SUCCESS) {
|
2001-05-06 14:35:20 +00:00
|
|
|
logevent
|
|
|
|
("Pageant's response accepted");
|
|
|
|
if (flags & FLAG_VERBOSE) {
|
2002-10-25 11:30:33 +00:00
|
|
|
c_write_str(ssh, "Authenticated using"
|
|
|
|
" RSA key \"");
|
2018-05-27 22:58:20 +00:00
|
|
|
c_write(ssh, s->comment.ptr,
|
|
|
|
s->comment.len);
|
2002-10-25 11:30:33 +00:00
|
|
|
c_write_str(ssh, "\" from agent\r\n");
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
2002-10-25 11:30:33 +00:00
|
|
|
s->authed = TRUE;
|
2001-05-06 14:35:20 +00:00
|
|
|
} else
|
|
|
|
logevent
|
|
|
|
("Pageant's response not accepted");
|
|
|
|
} else {
|
|
|
|
logevent
|
|
|
|
("Pageant failed to answer challenge");
|
|
|
|
sfree(ret);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
logevent("No reply received from Pageant");
|
|
|
|
}
|
|
|
|
}
|
2002-10-25 11:30:33 +00:00
|
|
|
freebn(s->key.exponent);
|
|
|
|
freebn(s->key.modulus);
|
|
|
|
freebn(s->challenge);
|
|
|
|
if (s->authed)
|
2001-05-06 14:35:20 +00:00
|
|
|
break;
|
|
|
|
}
|
2018-05-27 22:58:20 +00:00
|
|
|
sfree(s->agent_response);
|
2005-11-13 16:06:41 +00:00
|
|
|
if (s->publickey_blob && !s->tried_publickey)
|
|
|
|
logevent("Configured key file not in Pageant");
|
2011-06-08 20:39:06 +00:00
|
|
|
} else {
|
|
|
|
logevent("Failed to get reply from Pageant");
|
|
|
|
}
|
2002-10-25 11:30:33 +00:00
|
|
|
if (s->authed)
|
2001-05-06 14:35:20 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-05-12 11:30:25 +00:00
|
|
|
if (s->publickey_blob && s->privatekey_available &&
|
|
|
|
!s->tried_publickey) {
|
2005-10-30 20:24:09 +00:00
|
|
|
/*
|
|
|
|
* Try public key authentication with the specified
|
|
|
|
* key file.
|
|
|
|
*/
|
|
|
|
int got_passphrase; /* need not be kept over crReturn */
|
|
|
|
if (flags & FLAG_VERBOSE)
|
|
|
|
c_write_str(ssh, "Trying public key authentication.\r\n");
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
|
2005-10-30 20:24:09 +00:00
|
|
|
logeventf(ssh, "Trying public key \"%s\"",
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
filename_to_str(s->keyfile));
|
2005-10-30 20:24:09 +00:00
|
|
|
s->tried_publickey = 1;
|
|
|
|
got_passphrase = FALSE;
|
|
|
|
while (!got_passphrase) {
|
|
|
|
/*
|
|
|
|
* Get a passphrase, if necessary.
|
|
|
|
*/
|
|
|
|
char *passphrase = NULL; /* only written after crReturn */
|
|
|
|
const char *error;
|
2015-05-12 11:30:25 +00:00
|
|
|
if (!s->privatekey_encrypted) {
|
2005-10-30 20:24:09 +00:00
|
|
|
if (flags & FLAG_VERBOSE)
|
|
|
|
c_write_str(ssh, "No passphrase required.\r\n");
|
|
|
|
passphrase = NULL;
|
|
|
|
} else {
|
|
|
|
s->cur_prompt = new_prompts(ssh->frontend);
|
|
|
|
s->cur_prompt->to_server = FALSE;
|
|
|
|
s->cur_prompt->name = dupstr("SSH key passphrase");
|
|
|
|
add_prompt(s->cur_prompt,
|
|
|
|
dupprintf("Passphrase for key \"%.100s\": ",
|
2011-10-02 11:50:45 +00:00
|
|
|
s->publickey_comment), FALSE);
|
2018-05-18 06:22:58 +00:00
|
|
|
s->userpass_ret = get_userpass_input(s->cur_prompt, NULL);
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
while (1) {
|
2018-05-18 06:22:58 +00:00
|
|
|
while (s->userpass_ret < 0 &&
|
|
|
|
bufchain_size(&ssh->user_input) > 0)
|
|
|
|
s->userpass_ret = get_userpass_input(
|
|
|
|
s->cur_prompt, &ssh->user_input);
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
|
|
|
|
if (s->userpass_ret >= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ssh->send_ok = 1;
|
|
|
|
crReturnV;
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->send_ok = 0;
|
|
|
|
}
|
|
|
|
if (!s->userpass_ret) {
|
2005-10-30 20:24:09 +00:00
|
|
|
/* Failed to get a passphrase. Terminate. */
|
|
|
|
free_prompts(s->cur_prompt);
|
|
|
|
ssh_disconnect(ssh, NULL, "Unable to authenticate",
|
|
|
|
0, TRUE);
|
2018-05-18 06:22:58 +00:00
|
|
|
crStopV;
|
2005-10-30 20:24:09 +00:00
|
|
|
}
|
|
|
|
passphrase = dupstr(s->cur_prompt->prompts[0]->result);
|
|
|
|
free_prompts(s->cur_prompt);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Try decrypting key with passphrase.
|
|
|
|
*/
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
|
2018-05-24 07:22:44 +00:00
|
|
|
ret = rsa_ssh1_loadkey(
|
|
|
|
s->keyfile, &s->key, passphrase, &error);
|
2005-10-30 20:24:09 +00:00
|
|
|
if (passphrase) {
|
2012-07-22 19:51:50 +00:00
|
|
|
smemclr(passphrase, strlen(passphrase));
|
2005-10-30 20:24:09 +00:00
|
|
|
sfree(passphrase);
|
|
|
|
}
|
|
|
|
if (ret == 1) {
|
|
|
|
/* Correct passphrase. */
|
|
|
|
got_passphrase = TRUE;
|
|
|
|
} else if (ret == 0) {
|
|
|
|
c_write_str(ssh, "Couldn't load private key from ");
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
c_write_str(ssh, filename_to_str(s->keyfile));
|
2005-10-30 20:24:09 +00:00
|
|
|
c_write_str(ssh, " (");
|
|
|
|
c_write_str(ssh, error);
|
|
|
|
c_write_str(ssh, ").\r\n");
|
|
|
|
got_passphrase = FALSE;
|
|
|
|
break; /* go and try something else */
|
|
|
|
} else if (ret == -1) {
|
|
|
|
c_write_str(ssh, "Wrong passphrase.\r\n"); /* FIXME */
|
|
|
|
got_passphrase = FALSE;
|
|
|
|
/* and try again */
|
2005-11-04 23:21:42 +00:00
|
|
|
} else {
|
2018-05-24 07:22:44 +00:00
|
|
|
assert(0 && "unexpected return from rsa_ssh1_loadkey()");
|
2007-01-20 20:05:46 +00:00
|
|
|
got_passphrase = FALSE; /* placate optimisers */
|
2005-10-30 20:24:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (got_passphrase) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send a public key attempt.
|
|
|
|
*/
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_AUTH_RSA);
|
|
|
|
put_mp_ssh1(pkt, s->key.modulus);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2005-10-30 20:24:09 +00:00
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login))
|
|
|
|
!= NULL);
|
2005-10-30 20:24:09 +00:00
|
|
|
if (pktin->type == SSH1_SMSG_FAILURE) {
|
|
|
|
c_write_str(ssh, "Server refused our public key.\r\n");
|
2005-11-04 23:31:11 +00:00
|
|
|
continue; /* go and try something else */
|
2005-10-30 20:24:09 +00:00
|
|
|
}
|
|
|
|
if (pktin->type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {
|
|
|
|
bombout(("Bizarre response to offer of public key"));
|
2018-05-18 06:22:58 +00:00
|
|
|
crStopV;
|
2005-10-30 20:24:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
unsigned char buffer[32];
|
|
|
|
Bignum challenge, response;
|
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
challenge = get_mp_ssh1(pktin);
|
|
|
|
if (get_err(pktin)) {
|
|
|
|
freebn(challenge);
|
2005-10-30 20:24:09 +00:00
|
|
|
bombout(("Server's RSA challenge was badly formatted"));
|
2018-05-18 06:22:58 +00:00
|
|
|
crStopV;
|
2005-10-30 20:24:09 +00:00
|
|
|
}
|
2018-05-24 07:22:44 +00:00
|
|
|
response = rsa_ssh1_decrypt(challenge, &s->key);
|
2005-10-30 20:24:09 +00:00
|
|
|
freebn(s->key.private_exponent);/* burn the evidence */
|
|
|
|
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
|
|
buffer[i] = bignum_byte(response, 31 - i);
|
|
|
|
}
|
|
|
|
|
|
|
|
MD5Init(&md5c);
|
2018-05-24 09:03:36 +00:00
|
|
|
put_data(&md5c, buffer, 32);
|
|
|
|
put_data(&md5c, s->session_id, 16);
|
2005-10-30 20:24:09 +00:00
|
|
|
MD5Final(buffer, &md5c);
|
|
|
|
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH1_CMSG_AUTH_RSA_RESPONSE);
|
|
|
|
put_data(pkt, buffer, 16);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2005-10-30 20:24:09 +00:00
|
|
|
|
|
|
|
freebn(challenge);
|
|
|
|
freebn(response);
|
|
|
|
}
|
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login))
|
|
|
|
!= NULL);
|
2005-10-30 20:24:09 +00:00
|
|
|
if (pktin->type == SSH1_SMSG_FAILURE) {
|
|
|
|
if (flags & FLAG_VERBOSE)
|
|
|
|
c_write_str(ssh, "Failed to authenticate with"
|
|
|
|
" our public key.\r\n");
|
2005-11-04 23:31:11 +00:00
|
|
|
continue; /* go and try something else */
|
2005-10-30 20:24:09 +00:00
|
|
|
} else if (pktin->type != SSH1_SMSG_SUCCESS) {
|
|
|
|
bombout(("Bizarre response to RSA authentication response"));
|
2018-05-18 06:22:58 +00:00
|
|
|
crStopV;
|
2005-10-30 20:24:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
break; /* we're through! */
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Otherwise, try various forms of password-like authentication.
|
|
|
|
*/
|
|
|
|
s->cur_prompt = new_prompts(ssh->frontend);
|
2001-05-06 14:35:20 +00:00
|
|
|
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (conf_get_int(ssh->conf, CONF_try_tis_auth) &&
|
2002-10-25 11:30:33 +00:00
|
|
|
(s->supported_auths_mask & (1 << SSH1_AUTH_TIS)) &&
|
|
|
|
!s->tis_auth_refused) {
|
|
|
|
s->pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE;
|
2001-05-06 14:35:20 +00:00
|
|
|
logevent("Requested TIS authentication");
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_AUTH_TIS);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login)) != NULL);
|
2004-11-24 18:45:52 +00:00
|
|
|
if (pktin->type != SSH1_SMSG_AUTH_TIS_CHALLENGE) {
|
2001-05-06 14:35:20 +00:00
|
|
|
logevent("TIS authentication declined");
|
|
|
|
if (flags & FLAG_INTERACTIVE)
|
2002-10-25 11:30:33 +00:00
|
|
|
c_write_str(ssh, "TIS authentication refused.\r\n");
|
|
|
|
s->tis_auth_refused = 1;
|
2001-09-08 12:50:15 +00:00
|
|
|
continue;
|
2001-05-06 14:35:20 +00:00
|
|
|
} else {
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen challenge;
|
2005-10-30 20:24:09 +00:00
|
|
|
char *instr_suf, *prompt;
|
2004-08-01 12:07:11 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
challenge = get_string(pktin);
|
|
|
|
if (get_err(pktin)) {
|
2004-08-01 12:07:11 +00:00
|
|
|
bombout(("TIS challenge packet was badly formed"));
|
2018-05-18 06:22:58 +00:00
|
|
|
crStopV;
|
2004-08-01 12:07:11 +00:00
|
|
|
}
|
2001-05-06 14:35:20 +00:00
|
|
|
logevent("Received TIS challenge");
|
2005-10-30 20:24:09 +00:00
|
|
|
s->cur_prompt->to_server = TRUE;
|
|
|
|
s->cur_prompt->name = dupstr("SSH TIS authentication");
|
2001-09-08 12:50:15 +00:00
|
|
|
/* Prompt heuristic comes from OpenSSH */
|
2018-05-27 17:13:53 +00:00
|
|
|
if (memchr(challenge.ptr, '\n', challenge.len)) {
|
2005-10-30 20:24:09 +00:00
|
|
|
instr_suf = dupstr("");
|
2018-05-27 17:13:53 +00:00
|
|
|
prompt = mkstr(challenge);
|
2005-10-30 20:24:09 +00:00
|
|
|
} else {
|
2018-05-27 17:13:53 +00:00
|
|
|
instr_suf = mkstr(challenge);
|
2005-10-30 20:24:09 +00:00
|
|
|
prompt = dupstr("Response: ");
|
|
|
|
}
|
|
|
|
s->cur_prompt->instruction =
|
|
|
|
dupprintf("Using TIS authentication.%s%s",
|
|
|
|
(*instr_suf) ? "\n" : "",
|
|
|
|
instr_suf);
|
|
|
|
s->cur_prompt->instr_reqd = TRUE;
|
2011-10-02 11:50:45 +00:00
|
|
|
add_prompt(s->cur_prompt, prompt, FALSE);
|
2005-10-30 20:24:09 +00:00
|
|
|
sfree(instr_suf);
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
|
|
|
}
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (conf_get_int(ssh->conf, CONF_try_tis_auth) &&
|
2002-10-25 11:30:33 +00:00
|
|
|
(s->supported_auths_mask & (1 << SSH1_AUTH_CCARD)) &&
|
|
|
|
!s->ccard_auth_refused) {
|
|
|
|
s->pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE;
|
2001-05-06 14:35:20 +00:00
|
|
|
logevent("Requested CryptoCard authentication");
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_AUTH_CCARD);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login)) != NULL);
|
2004-11-24 18:45:52 +00:00
|
|
|
if (pktin->type != SSH1_SMSG_AUTH_CCARD_CHALLENGE) {
|
2001-05-06 14:35:20 +00:00
|
|
|
logevent("CryptoCard authentication declined");
|
2002-10-25 11:30:33 +00:00
|
|
|
c_write_str(ssh, "CryptoCard authentication refused.\r\n");
|
|
|
|
s->ccard_auth_refused = 1;
|
2001-09-08 12:50:15 +00:00
|
|
|
continue;
|
2001-05-06 14:35:20 +00:00
|
|
|
} else {
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen challenge;
|
2005-10-30 20:24:09 +00:00
|
|
|
char *instr_suf, *prompt;
|
2004-08-01 12:07:11 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
challenge = get_string(pktin);
|
|
|
|
if (get_err(pktin)) {
|
2004-08-01 12:07:11 +00:00
|
|
|
bombout(("CryptoCard challenge packet was badly formed"));
|
2018-05-18 06:22:58 +00:00
|
|
|
crStopV;
|
2004-08-01 12:07:11 +00:00
|
|
|
}
|
2001-05-06 14:35:20 +00:00
|
|
|
logevent("Received CryptoCard challenge");
|
2005-10-30 20:24:09 +00:00
|
|
|
s->cur_prompt->to_server = TRUE;
|
|
|
|
s->cur_prompt->name = dupstr("SSH CryptoCard authentication");
|
|
|
|
s->cur_prompt->name_reqd = FALSE;
|
|
|
|
/* Prompt heuristic comes from OpenSSH */
|
2018-05-27 17:13:53 +00:00
|
|
|
if (memchr(challenge.ptr, '\n', challenge.len)) {
|
2005-10-30 20:24:09 +00:00
|
|
|
instr_suf = dupstr("");
|
2018-05-27 17:13:53 +00:00
|
|
|
prompt = mkstr(challenge);
|
2005-10-30 20:24:09 +00:00
|
|
|
} else {
|
2018-05-27 17:13:53 +00:00
|
|
|
instr_suf = mkstr(challenge);
|
2005-10-30 20:24:09 +00:00
|
|
|
prompt = dupstr("Response: ");
|
|
|
|
}
|
|
|
|
s->cur_prompt->instruction =
|
|
|
|
dupprintf("Using CryptoCard authentication.%s%s",
|
|
|
|
(*instr_suf) ? "\n" : "",
|
|
|
|
instr_suf);
|
|
|
|
s->cur_prompt->instr_reqd = TRUE;
|
2011-10-02 11:50:45 +00:00
|
|
|
add_prompt(s->cur_prompt, prompt, FALSE);
|
2005-10-30 20:24:09 +00:00
|
|
|
sfree(instr_suf);
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
|
|
|
}
|
2002-10-25 11:30:33 +00:00
|
|
|
if (s->pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) {
|
2007-09-20 21:07:24 +00:00
|
|
|
if ((s->supported_auths_mask & (1 << SSH1_AUTH_PASSWORD)) == 0) {
|
|
|
|
bombout(("No supported authentication methods available"));
|
2018-05-18 06:22:58 +00:00
|
|
|
crStopV;
|
2007-09-20 21:07:24 +00:00
|
|
|
}
|
2005-10-30 20:24:09 +00:00
|
|
|
s->cur_prompt->to_server = TRUE;
|
|
|
|
s->cur_prompt->name = dupstr("SSH password");
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ",
|
|
|
|
ssh->username, ssh->savedhost),
|
2011-10-02 11:50:45 +00:00
|
|
|
FALSE);
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
2000-09-25 10:14:53 +00:00
|
|
|
|
2001-09-08 12:50:15 +00:00
|
|
|
/*
|
|
|
|
* Show password prompt, having first obtained it via a TIS
|
|
|
|
* or CryptoCard exchange if we're doing TIS or CryptoCard
|
|
|
|
* authentication.
|
|
|
|
*/
|
2018-05-18 06:22:58 +00:00
|
|
|
s->userpass_ret = get_userpass_input(s->cur_prompt, NULL);
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
while (1) {
|
2018-05-18 06:22:58 +00:00
|
|
|
while (s->userpass_ret < 0 &&
|
|
|
|
bufchain_size(&ssh->user_input) > 0)
|
|
|
|
s->userpass_ret = get_userpass_input(
|
|
|
|
s->cur_prompt, &ssh->user_input);
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
|
|
|
|
if (s->userpass_ret >= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ssh->send_ok = 1;
|
|
|
|
crReturnV;
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->send_ok = 0;
|
|
|
|
}
|
|
|
|
if (!s->userpass_ret) {
|
|
|
|
/*
|
|
|
|
* Failed to get a password (for example
|
|
|
|
* because one was supplied on the command line
|
|
|
|
* which has already failed to work). Terminate.
|
|
|
|
*/
|
|
|
|
free_prompts(s->cur_prompt);
|
|
|
|
ssh_disconnect(ssh, NULL, "Unable to authenticate", 0, TRUE);
|
|
|
|
crStopV;
|
|
|
|
}
|
2001-05-06 14:35:20 +00:00
|
|
|
|
2005-10-30 20:24:09 +00:00
|
|
|
if (s->pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) {
|
2001-05-06 14:35:20 +00:00
|
|
|
/*
|
2005-10-30 20:24:09 +00:00
|
|
|
* Defence against traffic analysis: we send a
|
|
|
|
* whole bunch of packets containing strings of
|
|
|
|
* different lengths. One of these strings is the
|
|
|
|
* password, in a SSH1_CMSG_AUTH_PASSWORD packet.
|
|
|
|
* The others are all random data in
|
|
|
|
* SSH1_MSG_IGNORE packets. This way a passive
|
|
|
|
* listener can't tell which is the password, and
|
|
|
|
* hence can't deduce the password length.
|
|
|
|
*
|
|
|
|
* Anybody with a password length greater than 16
|
|
|
|
* bytes is going to have enough entropy in their
|
|
|
|
* password that a listener won't find it _that_
|
|
|
|
* much help to know how long it is. So what we'll
|
|
|
|
* do is:
|
|
|
|
*
|
|
|
|
* - if password length < 16, we send 15 packets
|
|
|
|
* containing string lengths 1 through 15
|
|
|
|
*
|
|
|
|
* - otherwise, we let N be the nearest multiple
|
|
|
|
* of 8 below the password length, and send 8
|
|
|
|
* packets containing string lengths N through
|
|
|
|
* N+7. This won't obscure the order of
|
|
|
|
* magnitude of the password length, but it will
|
|
|
|
* introduce a bit of extra uncertainty.
|
|
|
|
*
|
2006-10-22 19:51:28 +00:00
|
|
|
* A few servers can't deal with SSH1_MSG_IGNORE, at
|
|
|
|
* least in this context. For these servers, we need
|
|
|
|
* an alternative defence. We make use of the fact
|
|
|
|
* that the password is interpreted as a C string:
|
|
|
|
* so we can append a NUL, then some random data.
|
2005-10-30 20:24:09 +00:00
|
|
|
*
|
2006-10-22 19:51:28 +00:00
|
|
|
* A few servers can deal with neither SSH1_MSG_IGNORE
|
|
|
|
* here _nor_ a padded password string.
|
|
|
|
* For these servers we are left with no defences
|
2005-10-30 20:24:09 +00:00
|
|
|
* against password length sniffing.
|
2001-05-06 14:35:20 +00:00
|
|
|
*/
|
2006-10-22 19:51:28 +00:00
|
|
|
if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE) &&
|
|
|
|
!(ssh->remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) {
|
2001-05-06 14:35:20 +00:00
|
|
|
/*
|
2005-10-30 20:24:09 +00:00
|
|
|
* The server can deal with SSH1_MSG_IGNORE, so
|
|
|
|
* we can use the primary defence.
|
2001-05-06 14:35:20 +00:00
|
|
|
*/
|
2005-10-30 20:24:09 +00:00
|
|
|
int bottom, top, pwlen, i;
|
2001-05-06 14:35:20 +00:00
|
|
|
|
2005-10-30 20:24:09 +00:00
|
|
|
pwlen = strlen(s->cur_prompt->prompts[0]->result);
|
|
|
|
if (pwlen < 16) {
|
|
|
|
bottom = 0; /* zero length passwords are OK! :-) */
|
|
|
|
top = 15;
|
|
|
|
} else {
|
|
|
|
bottom = pwlen & ~7;
|
|
|
|
top = bottom + 7;
|
|
|
|
}
|
2001-05-06 14:35:20 +00:00
|
|
|
|
2005-10-30 20:24:09 +00:00
|
|
|
assert(pwlen >= bottom && pwlen <= top);
|
2001-05-06 14:35:20 +00:00
|
|
|
|
2005-10-30 20:24:09 +00:00
|
|
|
for (i = bottom; i <= top; i++) {
|
|
|
|
if (i == pwlen) {
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, s->pwpkt_type);
|
|
|
|
put_stringz(pkt, s->cur_prompt->prompts[0]->result);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2005-10-30 20:24:09 +00:00
|
|
|
} else {
|
2018-06-09 08:59:58 +00:00
|
|
|
strbuf *random_data = strbuf_new();
|
|
|
|
for (j = 0; j < i; j++)
|
|
|
|
put_byte(random_data, random_byte());
|
|
|
|
|
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_IGNORE);
|
|
|
|
put_stringsb(pkt, random_data);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
2005-10-30 20:24:09 +00:00
|
|
|
}
|
|
|
|
logevent("Sending password with camouflage packets");
|
|
|
|
}
|
|
|
|
else if (!(ssh->remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) {
|
|
|
|
/*
|
|
|
|
* The server can't deal with SSH1_MSG_IGNORE
|
|
|
|
* but can deal with padded passwords, so we
|
|
|
|
* can use the secondary defence.
|
|
|
|
*/
|
2018-06-09 08:59:58 +00:00
|
|
|
strbuf *padded_pw = strbuf_new();
|
|
|
|
|
2005-10-30 20:24:09 +00:00
|
|
|
logevent("Sending length-padded password");
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, s->pwpkt_type);
|
|
|
|
put_asciz(padded_pw, s->cur_prompt->prompts[0]->result);
|
|
|
|
do {
|
|
|
|
put_byte(padded_pw, random_byte());
|
|
|
|
} while (padded_pw->len % 64 != 0);
|
|
|
|
put_stringsb(pkt, padded_pw);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2005-10-30 20:24:09 +00:00
|
|
|
} else {
|
|
|
|
/*
|
2006-10-22 19:51:28 +00:00
|
|
|
* The server is believed unable to cope with
|
|
|
|
* any of our password camouflage methods.
|
2005-10-30 20:24:09 +00:00
|
|
|
*/
|
|
|
|
logevent("Sending unpadded password");
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, s->pwpkt_type);
|
|
|
|
put_stringz(pkt, s->cur_prompt->prompts[0]->result);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
2005-10-30 20:24:09 +00:00
|
|
|
} else {
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, s->pwpkt_type);
|
|
|
|
put_stringz(pkt, s->cur_prompt->prompts[0]->result);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
1999-11-09 11:10:04 +00:00
|
|
|
logevent("Sent password");
|
2005-10-30 20:24:09 +00:00
|
|
|
free_prompts(s->cur_prompt);
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_login)) != NULL);
|
2004-11-24 18:45:52 +00:00
|
|
|
if (pktin->type == SSH1_SMSG_FAILURE) {
|
2001-05-06 14:35:20 +00:00
|
|
|
if (flags & FLAG_VERBOSE)
|
2002-10-25 11:30:33 +00:00
|
|
|
c_write_str(ssh, "Access denied\r\n");
|
1999-11-09 11:10:04 +00:00
|
|
|
logevent("Authentication refused");
|
2004-11-24 18:45:52 +00:00
|
|
|
} else if (pktin->type != SSH1_SMSG_SUCCESS) {
|
|
|
|
bombout(("Strange packet received, type %d", pktin->type));
|
2018-05-18 06:22:58 +00:00
|
|
|
crStopV;
|
1999-01-08 13:02:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-10-30 20:24:09 +00:00
|
|
|
/* Clear up */
|
|
|
|
if (s->publickey_blob) {
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf_free(s->publickey_blob);
|
2005-10-30 20:24:09 +00:00
|
|
|
sfree(s->publickey_comment);
|
|
|
|
}
|
|
|
|
|
1999-11-09 11:10:04 +00:00
|
|
|
logevent("Authentication successful");
|
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
/* Set up for the next phase */
|
|
|
|
{
|
|
|
|
int i;
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
for (i = 0; i < SSH_MAX_MSG; i++)
|
2018-05-18 06:22:58 +00:00
|
|
|
if (ssh->packet_dispatch[i] == ssh1_coro_wrapper_initial)
|
|
|
|
ssh->packet_dispatch[i] = ssh1_coro_wrapper_session;
|
|
|
|
ssh->current_user_input_fn = ssh1_connection_input;
|
2018-05-18 10:40:21 +00:00
|
|
|
queue_idempotent_callback(&ssh->user_input_consumer);
|
2018-05-18 06:22:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
crFinishV;
|
2000-06-22 12:18:34 +00:00
|
|
|
}
|
|
|
|
|
2011-09-13 11:44:03 +00:00
|
|
|
static void ssh_channel_try_eof(struct ssh_channel *c)
|
|
|
|
{
|
|
|
|
Ssh ssh = c->ssh;
|
2018-06-09 08:59:58 +00:00
|
|
|
PktOut *pktout;
|
2011-09-13 11:44:03 +00:00
|
|
|
assert(c->pending_eof); /* precondition for calling us */
|
|
|
|
if (c->halfopen)
|
|
|
|
return; /* can't close: not even opened yet */
|
|
|
|
if (ssh->version == 2 && bufchain_size(&c->v.v2.outbuffer) > 0)
|
|
|
|
return; /* can't send EOF: pending outgoing data */
|
|
|
|
|
2012-08-25 21:06:48 +00:00
|
|
|
c->pending_eof = FALSE; /* we're about to send it */
|
2011-09-13 11:44:03 +00:00
|
|
|
if (ssh->version == 1) {
|
2018-06-09 08:59:58 +00:00
|
|
|
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_CLOSE);
|
|
|
|
put_uint32(pktout, c->remoteid);
|
|
|
|
ssh_pkt_write(ssh, pktout);
|
2011-09-13 11:44:03 +00:00
|
|
|
c->closes |= CLOSES_SENT_EOF;
|
|
|
|
} else {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_CHANNEL_EOF);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(pktout, c->remoteid);
|
2011-09-13 11:44:03 +00:00
|
|
|
ssh2_pkt_send(ssh, pktout);
|
|
|
|
c->closes |= CLOSES_SENT_EOF;
|
2012-08-25 21:06:48 +00:00
|
|
|
ssh2_channel_check_close(c);
|
2011-09-13 11:44:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-14 12:47:13 +00:00
|
|
|
static Conf *sshchannel_get_conf(SshChannel *sc)
|
2013-11-17 14:05:04 +00:00
|
|
|
{
|
2018-09-14 12:47:13 +00:00
|
|
|
struct ssh_channel *c = FROMFIELD(sc, struct ssh_channel, sc);
|
2013-11-17 14:05:04 +00:00
|
|
|
Ssh ssh = c->ssh;
|
|
|
|
return ssh->conf;
|
|
|
|
}
|
|
|
|
|
2018-09-14 12:47:13 +00:00
|
|
|
static void sshchannel_write_eof(SshChannel *sc)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2018-09-14 12:47:13 +00:00
|
|
|
struct ssh_channel *c = FROMFIELD(sc, struct ssh_channel, sc);
|
2002-10-25 11:30:33 +00:00
|
|
|
Ssh ssh = c->ssh;
|
|
|
|
|
2005-03-05 18:14:57 +00:00
|
|
|
if (ssh->state == SSH_STATE_CLOSED)
|
2003-04-25 11:48:24 +00:00
|
|
|
return;
|
|
|
|
|
2011-09-13 11:44:03 +00:00
|
|
|
if (c->closes & CLOSES_SENT_EOF)
|
|
|
|
return;
|
2010-07-04 22:53:53 +00:00
|
|
|
|
2011-09-13 11:44:03 +00:00
|
|
|
c->pending_eof = TRUE;
|
|
|
|
ssh_channel_try_eof(c);
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 12:47:13 +00:00
|
|
|
static void sshchannel_unclean_close(SshChannel *sc, const char *err)
|
2011-12-08 19:15:58 +00:00
|
|
|
{
|
2018-09-14 12:47:13 +00:00
|
|
|
struct ssh_channel *c = FROMFIELD(sc, struct ssh_channel, sc);
|
2011-12-08 19:15:58 +00:00
|
|
|
Ssh ssh = c->ssh;
|
2016-05-28 13:50:02 +00:00
|
|
|
char *reason;
|
2011-12-08 19:15:58 +00:00
|
|
|
|
|
|
|
if (ssh->state == SSH_STATE_CLOSED)
|
|
|
|
return;
|
|
|
|
|
2016-05-28 13:50:02 +00:00
|
|
|
reason = dupprintf("due to local error: %s", err);
|
|
|
|
ssh_channel_close_local(c, reason);
|
|
|
|
sfree(reason);
|
2013-09-08 13:20:49 +00:00
|
|
|
c->pending_eof = FALSE; /* this will confuse a zombie channel */
|
2012-01-26 18:22:28 +00:00
|
|
|
|
2011-12-08 19:15:58 +00:00
|
|
|
ssh2_channel_check_close(c);
|
|
|
|
}
|
|
|
|
|
2018-09-14 12:47:13 +00:00
|
|
|
static int sshchannel_write(SshChannel *sc, const void *buf, int len)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2018-09-14 12:47:13 +00:00
|
|
|
struct ssh_channel *c = FROMFIELD(sc, struct ssh_channel, sc);
|
2002-10-25 11:30:33 +00:00
|
|
|
Ssh ssh = c->ssh;
|
|
|
|
|
2005-03-05 18:14:57 +00:00
|
|
|
if (ssh->state == SSH_STATE_CLOSED)
|
2003-04-25 11:48:24 +00:00
|
|
|
return 0;
|
|
|
|
|
2016-04-23 14:51:45 +00:00
|
|
|
return ssh_send_channel_data(c, buf, len);
|
2001-08-25 17:09:23 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 12:47:13 +00:00
|
|
|
static void sshchannel_unthrottle(SshChannel *sc, int bufsize)
|
2001-08-25 17:09:23 +00:00
|
|
|
{
|
2018-09-14 12:47:13 +00:00
|
|
|
struct ssh_channel *c = FROMFIELD(sc, struct ssh_channel, sc);
|
2002-10-25 11:30:33 +00:00
|
|
|
Ssh ssh = c->ssh;
|
|
|
|
|
2005-03-05 18:14:57 +00:00
|
|
|
if (ssh->state == SSH_STATE_CLOSED)
|
2003-04-25 11:48:24 +00:00
|
|
|
return;
|
|
|
|
|
2016-05-17 13:57:51 +00:00
|
|
|
ssh_channel_unthrottle(c, bufsize);
|
2001-01-22 11:34:52 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh_queueing_handler(Ssh ssh, PktIn *pktin)
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
{
|
|
|
|
struct queued_handler *qh = ssh->qhead;
|
|
|
|
|
|
|
|
assert(qh != NULL);
|
|
|
|
|
|
|
|
assert(pktin->type == qh->msg1 || pktin->type == qh->msg2);
|
|
|
|
|
|
|
|
if (qh->msg1 > 0) {
|
|
|
|
assert(ssh->packet_dispatch[qh->msg1] == ssh_queueing_handler);
|
2012-08-21 22:04:56 +00:00
|
|
|
ssh->packet_dispatch[qh->msg1] = ssh->q_saved_handler1;
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
}
|
|
|
|
if (qh->msg2 > 0) {
|
|
|
|
assert(ssh->packet_dispatch[qh->msg2] == ssh_queueing_handler);
|
2012-08-21 22:04:56 +00:00
|
|
|
ssh->packet_dispatch[qh->msg2] = ssh->q_saved_handler2;
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (qh->next) {
|
|
|
|
ssh->qhead = qh->next;
|
|
|
|
|
|
|
|
if (ssh->qhead->msg1 > 0) {
|
2012-08-21 22:04:56 +00:00
|
|
|
ssh->q_saved_handler1 = ssh->packet_dispatch[ssh->qhead->msg1];
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
ssh->packet_dispatch[ssh->qhead->msg1] = ssh_queueing_handler;
|
|
|
|
}
|
|
|
|
if (ssh->qhead->msg2 > 0) {
|
2012-08-21 22:04:56 +00:00
|
|
|
ssh->q_saved_handler2 = ssh->packet_dispatch[ssh->qhead->msg2];
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
ssh->packet_dispatch[ssh->qhead->msg2] = ssh_queueing_handler;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ssh->qhead = ssh->qtail = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
qh->handler(ssh, pktin, qh->ctx);
|
|
|
|
|
|
|
|
sfree(qh);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ssh_queue_handler(Ssh ssh, int msg1, int msg2,
|
|
|
|
chandler_fn_t handler, void *ctx)
|
|
|
|
{
|
|
|
|
struct queued_handler *qh;
|
|
|
|
|
|
|
|
qh = snew(struct queued_handler);
|
|
|
|
qh->msg1 = msg1;
|
|
|
|
qh->msg2 = msg2;
|
|
|
|
qh->handler = handler;
|
|
|
|
qh->ctx = ctx;
|
|
|
|
qh->next = NULL;
|
|
|
|
|
|
|
|
if (ssh->qtail == NULL) {
|
|
|
|
ssh->qhead = qh;
|
|
|
|
|
|
|
|
if (qh->msg1 > 0) {
|
2012-08-21 22:04:56 +00:00
|
|
|
ssh->q_saved_handler1 = ssh->packet_dispatch[ssh->qhead->msg1];
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
ssh->packet_dispatch[qh->msg1] = ssh_queueing_handler;
|
|
|
|
}
|
|
|
|
if (qh->msg2 > 0) {
|
2012-08-21 22:04:56 +00:00
|
|
|
ssh->q_saved_handler2 = ssh->packet_dispatch[ssh->qhead->msg2];
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
ssh->packet_dispatch[qh->msg2] = ssh_queueing_handler;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ssh->qtail->next = qh;
|
|
|
|
}
|
|
|
|
ssh->qtail = qh;
|
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh_rportfwd_succfail(Ssh ssh, PktIn *pktin, void *ctx)
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
{
|
2018-09-14 16:04:39 +00:00
|
|
|
struct ssh_rportfwd *rpf = (struct ssh_rportfwd *)ctx;
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
|
|
|
|
if (pktin->type == (ssh->version == 1 ? SSH1_SMSG_SUCCESS :
|
|
|
|
SSH2_MSG_REQUEST_SUCCESS)) {
|
|
|
|
logeventf(ssh, "Remote port forwarding from %s enabled",
|
2018-09-14 16:04:39 +00:00
|
|
|
rpf->log_description);
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
} else {
|
|
|
|
logeventf(ssh, "Remote port forwarding from %s refused",
|
2018-09-14 16:04:39 +00:00
|
|
|
rpf->log_description);
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
|
2018-09-14 16:04:39 +00:00
|
|
|
struct ssh_rportfwd *realpf = del234(ssh->rportfwds, rpf);
|
|
|
|
assert(realpf == rpf);
|
|
|
|
portfwdmgr_close(ssh->portfwdmgr, rpf->pfr);
|
|
|
|
free_rportfwd(rpf);
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-17 11:14:00 +00:00
|
|
|
/* Many of these vtable methods have the same names as wrapper macros
|
|
|
|
* in ssh.h, so parenthesise the names to inhibit macro expansion */
|
|
|
|
|
|
|
|
static struct ssh_rportfwd *(ssh_rportfwd_alloc)(
|
|
|
|
ConnectionLayer *cl,
|
|
|
|
const char *shost, int sport, const char *dhost, int dport,
|
|
|
|
int addressfamily, const char *log_description, PortFwdRecord *pfr,
|
|
|
|
ssh_sharing_connstate *share_ctx);
|
|
|
|
static void (ssh_rportfwd_remove)(
|
|
|
|
ConnectionLayer *cl, struct ssh_rportfwd *rpf);
|
|
|
|
static SshChannel *(ssh_lportfwd_open)(
|
|
|
|
ConnectionLayer *cl, const char *hostname, int port,
|
|
|
|
const char *org, Channel *chan);
|
|
|
|
static struct X11FakeAuth *(ssh_add_sharing_x11_display)(
|
|
|
|
ConnectionLayer *cl, int authtype, ssh_sharing_connstate *share_cs,
|
|
|
|
share_channel *share_chan);
|
|
|
|
static void (ssh_remove_sharing_x11_display)(ConnectionLayer *cl,
|
|
|
|
struct X11FakeAuth *auth);
|
|
|
|
static void (ssh_send_packet_from_downstream)(
|
|
|
|
ConnectionLayer *cl, unsigned id, int type,
|
|
|
|
const void *pkt, int pktlen, const char *additional_log_text);
|
|
|
|
static unsigned (ssh_alloc_sharing_channel)(
|
|
|
|
ConnectionLayer *cl, ssh_sharing_connstate *connstate);
|
|
|
|
static void (ssh_delete_sharing_channel)(
|
|
|
|
ConnectionLayer *cl, unsigned localid);
|
|
|
|
static void (ssh_sharing_queue_global_request)(
|
|
|
|
ConnectionLayer *cl, ssh_sharing_connstate *share_ctx);
|
|
|
|
static int (ssh_agent_forwarding_permitted)(ConnectionLayer *cl);
|
|
|
|
|
|
|
|
const struct ConnectionLayerVtable ssh_connlayer_vtable = {
|
|
|
|
ssh_rportfwd_alloc,
|
|
|
|
ssh_rportfwd_remove,
|
|
|
|
ssh_lportfwd_open,
|
|
|
|
ssh_add_sharing_x11_display,
|
|
|
|
ssh_remove_sharing_x11_display,
|
|
|
|
ssh_send_packet_from_downstream,
|
|
|
|
ssh_alloc_sharing_channel,
|
|
|
|
ssh_delete_sharing_channel,
|
|
|
|
ssh_sharing_queue_global_request,
|
|
|
|
ssh_agent_forwarding_permitted,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct ssh_rportfwd *(ssh_rportfwd_alloc)(
|
|
|
|
ConnectionLayer *cl,
|
|
|
|
const char *shost, int sport, const char *dhost, int dport,
|
2018-09-14 16:04:39 +00:00
|
|
|
int addressfamily, const char *log_description, PortFwdRecord *pfr,
|
|
|
|
ssh_sharing_connstate *share_ctx)
|
2013-11-17 14:05:41 +00:00
|
|
|
{
|
2018-09-17 11:14:00 +00:00
|
|
|
Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
|
|
|
|
|
2018-09-14 16:04:39 +00:00
|
|
|
/*
|
|
|
|
* Ensure the remote port forwardings tree exists.
|
|
|
|
*/
|
2013-11-17 14:05:41 +00:00
|
|
|
if (!ssh->rportfwds) {
|
2018-09-14 16:04:39 +00:00
|
|
|
if (ssh->version == 1)
|
|
|
|
ssh->rportfwds = newtree234(ssh_rportcmp_ssh1);
|
|
|
|
else
|
|
|
|
ssh->rportfwds = newtree234(ssh_rportcmp_ssh2);
|
2013-11-17 14:05:41 +00:00
|
|
|
}
|
2018-09-14 16:04:39 +00:00
|
|
|
|
|
|
|
struct ssh_rportfwd *rpf = snew(struct ssh_rportfwd);
|
|
|
|
|
|
|
|
rpf->shost = dupstr(shost);
|
|
|
|
rpf->sport = sport;
|
|
|
|
rpf->dhost = dupstr(dhost);
|
|
|
|
rpf->dport = dport;
|
|
|
|
rpf->addressfamily = addressfamily;
|
|
|
|
rpf->log_description = dupstr(log_description);
|
|
|
|
rpf->pfr = pfr;
|
|
|
|
rpf->share_ctx = share_ctx;
|
|
|
|
|
|
|
|
if (add234(ssh->rportfwds, rpf) != rpf) {
|
|
|
|
free_rportfwd(rpf);
|
|
|
|
return NULL;
|
2013-11-17 14:05:41 +00:00
|
|
|
}
|
2018-09-14 16:04:39 +00:00
|
|
|
|
|
|
|
if (!rpf->share_ctx) {
|
|
|
|
PktOut *pktout;
|
|
|
|
|
|
|
|
if (ssh->version == 1) {
|
|
|
|
pktout = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH1_CMSG_PORT_FORWARD_REQUEST);
|
|
|
|
put_uint32(pktout, rpf->sport);
|
|
|
|
put_stringz(pktout, rpf->dhost);
|
|
|
|
put_uint32(pktout, rpf->dport);
|
|
|
|
ssh_pkt_write(ssh, pktout);
|
|
|
|
ssh_queue_handler(ssh, SSH1_SMSG_SUCCESS,
|
|
|
|
SSH1_SMSG_FAILURE,
|
|
|
|
ssh_rportfwd_succfail, rpf);
|
|
|
|
} else {
|
|
|
|
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_GLOBAL_REQUEST);
|
|
|
|
put_stringz(pktout, "tcpip-forward");
|
|
|
|
put_bool(pktout, 1); /* want reply */
|
|
|
|
put_stringz(pktout, rpf->shost);
|
|
|
|
put_uint32(pktout, rpf->sport);
|
|
|
|
ssh2_pkt_send(ssh, pktout);
|
|
|
|
|
|
|
|
ssh_queue_handler(ssh, SSH2_MSG_REQUEST_SUCCESS,
|
|
|
|
SSH2_MSG_REQUEST_FAILURE,
|
|
|
|
ssh_rportfwd_succfail, rpf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return rpf;
|
2013-11-17 14:05:41 +00:00
|
|
|
}
|
|
|
|
|
2018-09-17 11:14:00 +00:00
|
|
|
static void (ssh_rportfwd_remove)(
|
|
|
|
ConnectionLayer *cl, struct ssh_rportfwd *rpf)
|
2018-06-03 06:54:00 +00:00
|
|
|
{
|
2018-09-17 11:14:00 +00:00
|
|
|
Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
|
2018-09-14 16:04:39 +00:00
|
|
|
if (ssh->version == 1) {
|
|
|
|
/*
|
|
|
|
* We cannot cancel listening ports on the server side in
|
|
|
|
* SSH-1! There's no message to support it.
|
|
|
|
*/
|
|
|
|
} else if (rpf->share_ctx) {
|
|
|
|
/*
|
|
|
|
* We don't manufacture a cancel-tcpip-forward message for
|
|
|
|
* remote port forwardings being removed on behalf of a
|
|
|
|
* downstream; we just pass through the one the downstream
|
|
|
|
* sent to us.
|
|
|
|
*/
|
|
|
|
} else {
|
|
|
|
PktOut *pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_GLOBAL_REQUEST);
|
|
|
|
put_stringz(pktout, "cancel-tcpip-forward");
|
|
|
|
put_bool(pktout, 0); /* _don't_ want reply */
|
|
|
|
put_stringz(pktout, rpf->shost);
|
|
|
|
put_uint32(pktout, rpf->sport);
|
|
|
|
ssh2_pkt_send(ssh, pktout);
|
|
|
|
}
|
2018-06-03 06:54:00 +00:00
|
|
|
|
2018-09-14 16:04:39 +00:00
|
|
|
struct ssh_rportfwd *realpf = del234(ssh->rportfwds, rpf);
|
|
|
|
assert(realpf == rpf);
|
|
|
|
free_rportfwd(rpf);
|
2018-06-03 06:54:00 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh_sharing_global_request_response(Ssh ssh, PktIn *pktin,
|
2013-11-17 14:05:41 +00:00
|
|
|
void *ctx)
|
|
|
|
{
|
|
|
|
share_got_pkt_from_server(ctx, pktin->type,
|
2018-06-06 06:17:09 +00:00
|
|
|
BinarySource_UPCAST(pktin)->data,
|
|
|
|
BinarySource_UPCAST(pktin)->len);
|
2013-11-17 14:05:41 +00:00
|
|
|
}
|
|
|
|
|
2018-09-17 11:14:00 +00:00
|
|
|
static void (ssh_sharing_queue_global_request)(
|
|
|
|
ConnectionLayer *cl, ssh_sharing_connstate *share_ctx)
|
2013-11-17 14:05:41 +00:00
|
|
|
{
|
2018-09-17 11:14:00 +00:00
|
|
|
Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
|
2013-11-17 14:05:41 +00:00
|
|
|
ssh_queue_handler(ssh, SSH2_MSG_REQUEST_SUCCESS, SSH2_MSG_REQUEST_FAILURE,
|
|
|
|
ssh_sharing_global_request_response, share_ctx);
|
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh1_smsg_stdout_stderr_data(Ssh ssh, PktIn *pktin)
|
2004-11-27 15:32:45 +00:00
|
|
|
{
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen string;
|
|
|
|
int bufsize;
|
2004-11-27 15:32:45 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
string = get_string(pktin);
|
|
|
|
if (get_err(pktin)) {
|
2004-11-27 15:32:45 +00:00
|
|
|
bombout(("Incoming terminal data packet was badly formed"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bufsize = from_backend(ssh->frontend, pktin->type == SSH1_SMSG_STDERR_DATA,
|
2018-05-27 17:13:53 +00:00
|
|
|
string.ptr, string.len);
|
2004-11-27 15:32:45 +00:00
|
|
|
if (!ssh->v1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) {
|
|
|
|
ssh->v1_stdout_throttling = 1;
|
2007-10-03 20:29:27 +00:00
|
|
|
ssh_throttle_conn(ssh, +1);
|
2004-11-27 15:32:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh1_smsg_x11_open(Ssh ssh, PktIn *pktin)
|
2004-11-27 15:32:45 +00:00
|
|
|
{
|
|
|
|
/* Remote side is trying to open a channel to talk to our
|
|
|
|
* X-Server. Give them back a local channel number. */
|
|
|
|
struct ssh_channel *c;
|
2018-06-09 08:59:58 +00:00
|
|
|
PktOut *pkt;
|
2018-05-27 17:13:53 +00:00
|
|
|
int remoteid = get_uint32(pktin);
|
2004-11-27 15:32:45 +00:00
|
|
|
|
|
|
|
logevent("Received X11 connect request");
|
|
|
|
/* Refuse if X11 forwarding is disabled. */
|
|
|
|
if (!ssh->X11_fwd_enabled) {
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_OPEN_FAILURE);
|
|
|
|
put_uint32(pkt, remoteid);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2004-11-27 15:32:45 +00:00
|
|
|
logevent("Rejected X11 connect request");
|
|
|
|
} else {
|
|
|
|
c = snew(struct ssh_channel);
|
|
|
|
c->ssh = ssh;
|
|
|
|
|
2016-05-21 21:29:57 +00:00
|
|
|
ssh_channel_init(c);
|
2018-09-14 12:47:13 +00:00
|
|
|
c->chan = x11_new_channel(ssh->x11authtree, &c->sc, NULL, -1, FALSE);
|
2013-11-17 14:05:23 +00:00
|
|
|
c->remoteid = remoteid;
|
|
|
|
c->halfopen = FALSE;
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);
|
|
|
|
put_uint32(pkt, c->remoteid);
|
|
|
|
put_uint32(pkt, c->localid);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2013-11-17 14:05:23 +00:00
|
|
|
logevent("Opened X11 forward channel");
|
2004-11-27 15:32:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh1_smsg_agent_open(Ssh ssh, PktIn *pktin)
|
2004-11-27 15:32:45 +00:00
|
|
|
{
|
|
|
|
/* Remote side is trying to open a channel to talk to our
|
|
|
|
* agent. Give them back a local channel number. */
|
|
|
|
struct ssh_channel *c;
|
2018-06-09 08:59:58 +00:00
|
|
|
PktOut *pkt;
|
2018-05-27 17:13:53 +00:00
|
|
|
int remoteid = toint(get_uint32(pktin));
|
2004-11-27 15:32:45 +00:00
|
|
|
|
|
|
|
/* Refuse if agent forwarding is disabled. */
|
|
|
|
if (!ssh->agentfwd_enabled) {
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_OPEN_FAILURE);
|
|
|
|
put_uint32(pkt, remoteid);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2004-11-27 15:32:45 +00:00
|
|
|
} else {
|
|
|
|
c = snew(struct ssh_channel);
|
|
|
|
c->ssh = ssh;
|
2016-05-21 21:29:57 +00:00
|
|
|
ssh_channel_init(c);
|
2004-11-27 15:32:45 +00:00
|
|
|
c->remoteid = remoteid;
|
2005-01-22 16:06:21 +00:00
|
|
|
c->halfopen = FALSE;
|
2018-09-14 12:47:13 +00:00
|
|
|
c->chan = agentf_new(&c->sc);
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);
|
|
|
|
put_uint32(pkt, c->remoteid);
|
|
|
|
put_uint32(pkt, c->localid);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2004-11-27 15:32:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh1_msg_port_open(Ssh ssh, PktIn *pktin)
|
2004-11-27 15:32:45 +00:00
|
|
|
{
|
|
|
|
/* Remote side is trying to open a channel to talk to a
|
|
|
|
* forwarded port. Give them back a local channel number. */
|
2004-12-30 16:45:11 +00:00
|
|
|
struct ssh_rportfwd pf, *pfp;
|
2018-06-09 08:59:58 +00:00
|
|
|
PktOut *pkt;
|
2004-11-27 15:32:45 +00:00
|
|
|
int remoteid;
|
2018-05-27 17:13:53 +00:00
|
|
|
int port;
|
|
|
|
ptrlen host;
|
Refactor ssh.c's APIs to x11fwd.c and portfwd.c.
The most important change is that, where previously ssh.c held the
Socket pointer for each X11 and port forwarding, and the support
modules would find their internal state structure by calling
sk_get_private_ptr on that Socket, it's now the other way round. ssh.c
now directly holds the internal state structure pointer for each
forwarding, and when the support module needs the Socket it looks it
up in a field of that. This will come in handy when I decouple socket
creation from logical forwarding setup, so that X forwardings can
delay actually opening a connection to an X server until they look at
the authentication data and see which server it has to be.
However, while I'm here, I've also taken the opportunity to clean up a
few other points, notably error message handling, and also the fact
that the same kind of state structure was used for both
connection-type and listening-type port forwardings. Now there are
separate PortForwarding and PortListener structure types, which seems
far more sensible.
[originally from svn r10074]
2013-11-17 14:04:41 +00:00
|
|
|
char *err;
|
2004-11-27 15:32:45 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
remoteid = toint(get_uint32(pktin));
|
|
|
|
host = get_string(pktin);
|
|
|
|
port = toint(get_uint32(pktin));
|
2004-11-27 15:32:45 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
pf.dhost = mkstr(host);
|
2004-11-27 15:32:45 +00:00
|
|
|
pf.dport = port;
|
2004-12-30 16:45:11 +00:00
|
|
|
pfp = find234(ssh->rportfwds, &pf, NULL);
|
2004-11-27 15:32:45 +00:00
|
|
|
|
2004-12-30 16:45:11 +00:00
|
|
|
if (pfp == NULL) {
|
2005-02-10 00:16:59 +00:00
|
|
|
logeventf(ssh, "Rejected remote port open request for %s:%d",
|
|
|
|
pf.dhost, port);
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_OPEN_FAILURE);
|
|
|
|
put_uint32(pkt, remoteid);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2004-11-27 15:32:45 +00:00
|
|
|
} else {
|
2013-07-14 10:46:07 +00:00
|
|
|
struct ssh_channel *c = snew(struct ssh_channel);
|
|
|
|
c->ssh = ssh;
|
|
|
|
|
2005-02-10 00:16:59 +00:00
|
|
|
logeventf(ssh, "Received remote port open request for %s:%d",
|
|
|
|
pf.dhost, port);
|
2018-09-14 16:04:39 +00:00
|
|
|
err = portfwdmgr_connect(ssh->portfwdmgr, &c->chan, pf.dhost, port,
|
|
|
|
&c->sc, pfp->addressfamily);
|
Refactor ssh.c's APIs to x11fwd.c and portfwd.c.
The most important change is that, where previously ssh.c held the
Socket pointer for each X11 and port forwarding, and the support
modules would find their internal state structure by calling
sk_get_private_ptr on that Socket, it's now the other way round. ssh.c
now directly holds the internal state structure pointer for each
forwarding, and when the support module needs the Socket it looks it
up in a field of that. This will come in handy when I decouple socket
creation from logical forwarding setup, so that X forwardings can
delay actually opening a connection to an X server until they look at
the authentication data and see which server it has to be.
However, while I'm here, I've also taken the opportunity to clean up a
few other points, notably error message handling, and also the fact
that the same kind of state structure was used for both
connection-type and listening-type port forwardings. Now there are
separate PortForwarding and PortListener structure types, which seems
far more sensible.
[originally from svn r10074]
2013-11-17 14:04:41 +00:00
|
|
|
if (err != NULL) {
|
|
|
|
logeventf(ssh, "Port open failed: %s", err);
|
|
|
|
sfree(err);
|
2004-11-27 15:32:45 +00:00
|
|
|
sfree(c);
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_CHANNEL_OPEN_FAILURE);
|
|
|
|
put_uint32(pkt, remoteid);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2004-11-27 15:32:45 +00:00
|
|
|
} else {
|
2016-05-21 21:29:57 +00:00
|
|
|
ssh_channel_init(c);
|
2004-11-27 15:32:45 +00:00
|
|
|
c->remoteid = remoteid;
|
2005-01-22 16:06:21 +00:00
|
|
|
c->halfopen = FALSE;
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);
|
|
|
|
put_uint32(pkt, c->remoteid);
|
|
|
|
put_uint32(pkt, c->localid);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2004-11-27 15:32:45 +00:00
|
|
|
logevent("Forwarded port opened successfully");
|
|
|
|
}
|
|
|
|
}
|
2013-11-17 14:03:21 +00:00
|
|
|
|
|
|
|
sfree(pf.dhost);
|
2004-11-27 15:32:45 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh1_msg_channel_open_confirmation(Ssh ssh, PktIn *pktin)
|
2004-11-27 15:32:45 +00:00
|
|
|
{
|
|
|
|
struct ssh_channel *c;
|
|
|
|
|
2016-05-22 21:21:20 +00:00
|
|
|
c = ssh_channel_msg(ssh, pktin);
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
chan_open_confirmation(c->chan);
|
|
|
|
c->remoteid = get_uint32(pktin);
|
|
|
|
c->halfopen = FALSE;
|
|
|
|
c->throttling_conn = 0;
|
2004-11-27 15:32:45 +00:00
|
|
|
|
2011-09-13 11:44:03 +00:00
|
|
|
if (c && c->pending_eof) {
|
2004-11-27 15:32:45 +00:00
|
|
|
/*
|
|
|
|
* We have a pending close on this channel,
|
|
|
|
* which we decided on before the server acked
|
|
|
|
* the channel open. So now we know the
|
|
|
|
* remoteid, we can close it again.
|
|
|
|
*/
|
2011-09-13 11:44:03 +00:00
|
|
|
ssh_channel_try_eof(c);
|
2004-11-27 15:32:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh1_msg_channel_open_failure(Ssh ssh, PktIn *pktin)
|
2004-11-27 15:32:45 +00:00
|
|
|
{
|
|
|
|
struct ssh_channel *c;
|
|
|
|
|
2016-05-22 21:21:20 +00:00
|
|
|
c = ssh_channel_msg(ssh, pktin);
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
chan_open_failed(c->chan, NULL);
|
|
|
|
chan_free(c->chan);
|
|
|
|
|
|
|
|
del234(ssh->channels, c);
|
|
|
|
sfree(c);
|
2004-11-27 15:32:45 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh1_msg_channel_close(Ssh ssh, PktIn *pktin)
|
2004-11-27 15:32:45 +00:00
|
|
|
{
|
|
|
|
/* Remote side closes a channel. */
|
|
|
|
struct ssh_channel *c;
|
2016-05-22 21:21:20 +00:00
|
|
|
|
|
|
|
c = ssh_channel_msg(ssh, pktin);
|
|
|
|
if (c) {
|
2004-11-27 15:32:45 +00:00
|
|
|
|
2016-05-25 22:16:09 +00:00
|
|
|
if (pktin->type == SSH1_MSG_CHANNEL_CLOSE) {
|
2011-09-13 11:44:03 +00:00
|
|
|
/*
|
|
|
|
* Received CHANNEL_CLOSE, which we translate into
|
|
|
|
* outgoing EOF.
|
|
|
|
*/
|
2016-05-25 22:16:09 +00:00
|
|
|
ssh_channel_got_eof(c);
|
2011-09-13 11:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pktin->type == SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION &&
|
|
|
|
!(c->closes & CLOSES_RCVD_CLOSE)) {
|
|
|
|
|
|
|
|
if (!(c->closes & CLOSES_SENT_EOF)) {
|
2016-05-22 21:21:20 +00:00
|
|
|
bombout(("Received CHANNEL_CLOSE_CONFIRMATION for channel %u"
|
|
|
|
" for which we never sent CHANNEL_CLOSE\n",
|
|
|
|
c->localid));
|
2011-09-13 11:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
c->closes |= CLOSES_RCVD_CLOSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!((CLOSES_SENT_EOF | CLOSES_RCVD_EOF) & ~c->closes) &&
|
|
|
|
!(c->closes & CLOSES_SENT_CLOSE)) {
|
2018-06-09 08:59:58 +00:00
|
|
|
PktOut *pkt = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION);
|
|
|
|
put_uint32(pkt, c->remoteid);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2011-09-13 11:44:03 +00:00
|
|
|
c->closes |= CLOSES_SENT_CLOSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes))
|
|
|
|
ssh_channel_destroy(c);
|
2004-11-27 15:32:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-21 12:13:00 +00:00
|
|
|
static int ssh_channel_data(struct ssh_channel *c, int is_stderr,
|
2018-06-02 06:52:26 +00:00
|
|
|
const void *data, int length)
|
2016-05-21 12:13:00 +00:00
|
|
|
{
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
if (c->chan)
|
|
|
|
chan_send(c->chan, is_stderr, data, length);
|
2016-05-21 12:13:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh1_msg_channel_data(Ssh ssh, PktIn *pktin)
|
2004-11-27 15:32:45 +00:00
|
|
|
{
|
|
|
|
/* Data sent down one of our channels. */
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen data;
|
2004-11-27 15:32:45 +00:00
|
|
|
struct ssh_channel *c;
|
|
|
|
|
2016-05-22 21:21:20 +00:00
|
|
|
c = ssh_channel_msg(ssh, pktin);
|
2018-05-27 17:13:53 +00:00
|
|
|
data = get_string(pktin);
|
2004-11-27 15:32:45 +00:00
|
|
|
|
|
|
|
if (c) {
|
2018-05-27 17:13:53 +00:00
|
|
|
int bufsize = ssh_channel_data(c, FALSE, data.ptr, data.len);
|
2007-10-03 20:29:27 +00:00
|
|
|
if (!c->throttling_conn && bufsize > SSH1_BUFFER_LIMIT) {
|
|
|
|
c->throttling_conn = 1;
|
|
|
|
ssh_throttle_conn(ssh, +1);
|
2004-11-27 15:32:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh1_smsg_exit_status(Ssh ssh, PktIn *pktin)
|
2004-11-27 15:32:45 +00:00
|
|
|
{
|
2018-06-09 08:59:58 +00:00
|
|
|
PktOut *pkt;
|
2018-05-27 17:13:53 +00:00
|
|
|
ssh->exitcode = get_uint32(pktin);
|
2005-02-10 00:16:59 +00:00
|
|
|
logeventf(ssh, "Server sent command exit status %d", ssh->exitcode);
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_EXIT_CONFIRMATION);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2004-11-27 15:32:45 +00:00
|
|
|
/*
|
|
|
|
* In case `helpful' firewalls or proxies tack
|
|
|
|
* extra human-readable text on the end of the
|
|
|
|
* session which we might mistake for another
|
|
|
|
* encrypted packet, we close the session once
|
|
|
|
* we've sent EXIT_CONFIRMATION.
|
|
|
|
*/
|
2005-05-21 16:49:27 +00:00
|
|
|
ssh_disconnect(ssh, NULL, NULL, 0, TRUE);
|
2004-11-27 15:32:45 +00:00
|
|
|
}
|
|
|
|
|
2018-09-17 11:14:00 +00:00
|
|
|
static int (ssh_agent_forwarding_permitted)(ConnectionLayer *cl)
|
2013-11-17 14:05:41 +00:00
|
|
|
{
|
2018-09-17 11:14:00 +00:00
|
|
|
Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
|
2013-11-17 14:05:41 +00:00
|
|
|
return conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists();
|
|
|
|
}
|
2005-04-21 13:57:08 +00:00
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
static void do_ssh1_connection(void *vctx)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2018-05-18 06:22:58 +00:00
|
|
|
Ssh ssh = (Ssh)vctx;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktIn *pktin;
|
2018-06-09 08:59:58 +00:00
|
|
|
PktOut *pkt;
|
2018-05-18 06:22:58 +00:00
|
|
|
|
2004-11-24 20:35:15 +00:00
|
|
|
crBegin(ssh->do_ssh1_connection_crstate);
|
2000-06-22 12:18:34 +00:00
|
|
|
|
2004-11-27 15:32:45 +00:00
|
|
|
ssh->packet_dispatch[SSH1_SMSG_STDOUT_DATA] =
|
|
|
|
ssh->packet_dispatch[SSH1_SMSG_STDERR_DATA] =
|
|
|
|
ssh1_smsg_stdout_stderr_data;
|
|
|
|
|
|
|
|
ssh->packet_dispatch[SSH1_MSG_CHANNEL_OPEN_CONFIRMATION] =
|
|
|
|
ssh1_msg_channel_open_confirmation;
|
|
|
|
ssh->packet_dispatch[SSH1_MSG_CHANNEL_OPEN_FAILURE] =
|
|
|
|
ssh1_msg_channel_open_failure;
|
|
|
|
ssh->packet_dispatch[SSH1_MSG_CHANNEL_CLOSE] =
|
|
|
|
ssh->packet_dispatch[SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION] =
|
|
|
|
ssh1_msg_channel_close;
|
|
|
|
ssh->packet_dispatch[SSH1_MSG_CHANNEL_DATA] = ssh1_msg_channel_data;
|
|
|
|
ssh->packet_dispatch[SSH1_SMSG_EXIT_STATUS] = ssh1_smsg_exit_status;
|
|
|
|
|
2018-09-17 11:14:00 +00:00
|
|
|
if (ssh_agent_forwarding_permitted(&ssh->cl)) {
|
2001-05-06 14:35:20 +00:00
|
|
|
logevent("Requesting agent forwarding");
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_AGENT_REQUEST_FORWARDING);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_connection)) != NULL);
|
2004-11-24 18:45:52 +00:00
|
|
|
if (pktin->type != SSH1_SMSG_SUCCESS
|
|
|
|
&& pktin->type != SSH1_SMSG_FAILURE) {
|
2003-03-31 11:22:30 +00:00
|
|
|
bombout(("Protocol confusion"));
|
2003-04-23 13:48:09 +00:00
|
|
|
crStopV;
|
2004-11-24 18:45:52 +00:00
|
|
|
} else if (pktin->type == SSH1_SMSG_FAILURE) {
|
2001-05-06 14:35:20 +00:00
|
|
|
logevent("Agent forwarding refused");
|
|
|
|
} else {
|
|
|
|
logevent("Agent forwarding enabled");
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->agentfwd_enabled = TRUE;
|
2004-11-27 15:32:45 +00:00
|
|
|
ssh->packet_dispatch[SSH1_SMSG_AGENT_OPEN] = ssh1_smsg_agent_open;
|
2000-12-02 11:43:25 +00:00
|
|
|
}
|
2000-09-15 10:48:42 +00:00
|
|
|
}
|
|
|
|
|
2013-11-17 14:05:10 +00:00
|
|
|
if (conf_get_int(ssh->conf, CONF_x11_forward)) {
|
|
|
|
ssh->x11disp =
|
|
|
|
x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display),
|
|
|
|
ssh->conf);
|
|
|
|
if (!ssh->x11disp) {
|
|
|
|
/* FIXME: return an error message from x11_setup_display */
|
|
|
|
logevent("X11 forwarding not enabled: unable to"
|
|
|
|
" initialise X display");
|
|
|
|
} else {
|
|
|
|
ssh->x11auth = x11_invent_fake_auth
|
|
|
|
(ssh->x11authtree, conf_get_int(ssh->conf, CONF_x11_auth));
|
|
|
|
ssh->x11auth->disp = ssh->x11disp;
|
|
|
|
|
|
|
|
logevent("Requesting X11 forwarding");
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH1_CMSG_X11_REQUEST_FORWARDING);
|
|
|
|
put_stringz(pkt, ssh->x11auth->protoname);
|
|
|
|
put_stringz(pkt, ssh->x11auth->datastring);
|
|
|
|
if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER)
|
|
|
|
put_uint32(pkt, ssh->x11disp->screennum);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_connection))
|
|
|
|
!= NULL);
|
2013-11-17 14:05:10 +00:00
|
|
|
if (pktin->type != SSH1_SMSG_SUCCESS
|
|
|
|
&& pktin->type != SSH1_SMSG_FAILURE) {
|
|
|
|
bombout(("Protocol confusion"));
|
|
|
|
crStopV;
|
|
|
|
} else if (pktin->type == SSH1_SMSG_FAILURE) {
|
|
|
|
logevent("X11 forwarding refused");
|
|
|
|
} else {
|
|
|
|
logevent("X11 forwarding enabled");
|
|
|
|
ssh->X11_fwd_enabled = TRUE;
|
|
|
|
ssh->packet_dispatch[SSH1_SMSG_X11_OPEN] = ssh1_smsg_x11_open;
|
|
|
|
}
|
|
|
|
}
|
2001-01-22 11:34:52 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 16:04:39 +00:00
|
|
|
portfwdmgr_config(ssh->portfwdmgr, ssh->conf);
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
ssh->packet_dispatch[SSH1_MSG_PORT_OPEN] = ssh1_msg_port_open;
|
2001-08-08 20:44:35 +00:00
|
|
|
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (!conf_get_int(ssh->conf, CONF_nopty)) {
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pkt;
|
2004-04-24 20:05:03 +00:00
|
|
|
/* Unpick the terminal-speed string. */
|
|
|
|
/* XXX perhaps we should allow no speeds to be sent. */
|
2004-06-03 10:36:27 +00:00
|
|
|
ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
sscanf(conf_get_str(ssh->conf, CONF_termspeed), "%d,%d", &ssh->ospeed, &ssh->ispeed);
|
2004-04-24 20:05:03 +00:00
|
|
|
/* Send the pty request. */
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_REQUEST_PTY);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(pkt, conf_get_str(ssh->conf, CONF_termtype));
|
|
|
|
put_uint32(pkt, ssh->term_height);
|
|
|
|
put_uint32(pkt, ssh->term_width);
|
|
|
|
put_uint32(pkt, 0); /* width in pixels */
|
|
|
|
put_uint32(pkt, 0); /* height in pixels */
|
2018-09-19 19:36:40 +00:00
|
|
|
write_ttymodes_to_packet_from_conf(
|
|
|
|
BinarySink_UPCAST(pkt), ssh->frontend, ssh->conf,
|
|
|
|
1, ssh->ospeed, ssh->ispeed);
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh_pkt_write(ssh, pkt);
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->state = SSH_STATE_INTERMED;
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_connection)) != NULL);
|
2004-11-24 18:45:52 +00:00
|
|
|
if (pktin->type != SSH1_SMSG_SUCCESS
|
|
|
|
&& pktin->type != SSH1_SMSG_FAILURE) {
|
2003-03-31 11:22:30 +00:00
|
|
|
bombout(("Protocol confusion"));
|
2003-04-23 13:48:09 +00:00
|
|
|
crStopV;
|
2004-11-24 18:45:52 +00:00
|
|
|
} else if (pktin->type == SSH1_SMSG_FAILURE) {
|
2002-10-25 11:30:33 +00:00
|
|
|
c_write_str(ssh, "Server refused to allocate pty\r\n");
|
|
|
|
ssh->editing = ssh->echoing = 1;
|
2011-09-13 15:38:12 +00:00
|
|
|
} else {
|
|
|
|
logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)",
|
|
|
|
ssh->ospeed, ssh->ispeed);
|
|
|
|
ssh->got_pty = TRUE;
|
|
|
|
}
|
2001-01-24 14:08:20 +00:00
|
|
|
} else {
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->editing = ssh->echoing = 1;
|
1999-01-08 13:02:13 +00:00
|
|
|
}
|
|
|
|
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (conf_get_int(ssh->conf, CONF_compression)) {
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_REQUEST_COMPRESSION);
|
|
|
|
put_uint32(pkt, 6); /* gzip compression level */
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_connection)) != NULL);
|
2004-11-24 18:45:52 +00:00
|
|
|
if (pktin->type != SSH1_SMSG_SUCCESS
|
|
|
|
&& pktin->type != SSH1_SMSG_FAILURE) {
|
2003-03-31 11:22:30 +00:00
|
|
|
bombout(("Protocol confusion"));
|
2003-04-23 13:48:09 +00:00
|
|
|
crStopV;
|
2004-11-24 18:45:52 +00:00
|
|
|
} else if (pktin->type == SSH1_SMSG_FAILURE) {
|
2002-10-25 11:30:33 +00:00
|
|
|
c_write_str(ssh, "Server refused to compress\r\n");
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
logevent("Started zlib (RFC1950) compression");
|
|
|
|
ssh1_bpp_start_compression(ssh->bpp);
|
2000-11-01 21:34:21 +00:00
|
|
|
}
|
|
|
|
|
2001-08-26 18:32:28 +00:00
|
|
|
/*
|
|
|
|
* Start the shell or command.
|
|
|
|
*
|
2005-03-10 16:36:05 +00:00
|
|
|
* Special case: if the first-choice command is an SSH-2
|
2001-08-26 18:32:28 +00:00
|
|
|
* subsystem (hence not usable here) and the second choice
|
|
|
|
* exists, we fall straight back to that.
|
|
|
|
*/
|
|
|
|
{
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
char *cmd = conf_get_str(ssh->conf, CONF_remote_cmd);
|
2001-08-26 18:32:28 +00:00
|
|
|
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (conf_get_int(ssh->conf, CONF_ssh_subsys) &&
|
|
|
|
conf_get_str(ssh->conf, CONF_remote_cmd2)) {
|
|
|
|
cmd = conf_get_str(ssh->conf, CONF_remote_cmd2);
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->fallback_cmd = TRUE;
|
2001-08-26 18:32:28 +00:00
|
|
|
}
|
2018-06-09 08:59:58 +00:00
|
|
|
if (*cmd) {
|
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_EXEC_CMD);
|
|
|
|
put_stringz(pkt, cmd);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
|
|
|
} else {
|
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_EXEC_SHELL);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
|
|
|
}
|
2001-08-26 18:32:28 +00:00
|
|
|
logevent("Started session");
|
|
|
|
}
|
1999-01-08 13:02:13 +00:00
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->state = SSH_STATE_SESSION;
|
|
|
|
if (ssh->size_needed)
|
2018-09-11 15:23:38 +00:00
|
|
|
backend_size(&ssh->backend, ssh->term_width, ssh->term_height);
|
2002-10-25 11:30:33 +00:00
|
|
|
if (ssh->eof_needed)
|
2018-09-11 15:23:38 +00:00
|
|
|
backend_special(&ssh->backend, TS_EOF);
|
1999-01-08 13:02:13 +00:00
|
|
|
|
2002-10-26 10:16:19 +00:00
|
|
|
if (ssh->ldisc)
|
Move echo/edit state change functionality out of ldisc_send.
I'm not actually sure why we've always had back ends notify ldisc of
changes to echo/edit settings by giving ldisc_send(ldisc,NULL,0,0) a
special meaning, instead of by having a separate dedicated notify
function with its own prototype and parameter set. Coverity's recent
observation that the two kinds of call don't even have the same
requirements on the ldisc (particularly, whether ldisc->term can be
NULL) makes me realise that it's really high time I separated the two
conceptually different operations into actually different functions.
While I'm here, I've renamed the confusing ldisc_update() function
which that special operation ends up feeding to, because it's not
actually a function applying to an ldisc - it applies to a front end.
So ldisc_send(ldisc,NULL,0,0) is now ldisc_echoedit_update(ldisc), and
that in turn figures out the current echo/edit settings before passing
them on to frontend_echoedit_update(). I think that should be clearer.
2014-11-22 16:12:47 +00:00
|
|
|
ldisc_echoedit_update(ssh->ldisc); /* cause ldisc to notice changes */
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->send_ok = 1;
|
|
|
|
ssh->channels = newtree234(ssh_channelcmp);
|
1999-01-08 13:02:13 +00:00
|
|
|
while (1) {
|
2001-08-08 20:44:35 +00:00
|
|
|
|
2004-11-27 15:32:45 +00:00
|
|
|
/*
|
|
|
|
* By this point, most incoming packets are already being
|
|
|
|
* handled by the dispatch table, and we need only pay
|
|
|
|
* attention to the unusual ones.
|
|
|
|
*/
|
2002-09-15 13:24:00 +00:00
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
while ((pktin = pq_pop(&ssh->pq_ssh1_connection)) != NULL) {
|
2004-11-27 15:32:45 +00:00
|
|
|
if (pktin->type == SSH1_SMSG_SUCCESS) {
|
1999-11-08 17:36:08 +00:00
|
|
|
/* may be from EXEC_SHELL on some servers */
|
2004-11-24 18:45:52 +00:00
|
|
|
} else if (pktin->type == SSH1_SMSG_FAILURE) {
|
1999-11-08 17:36:08 +00:00
|
|
|
/* may be from EXEC_SHELL on some servers
|
1999-01-08 13:02:13 +00:00
|
|
|
* if no pty is available or in other odd cases. Ignore */
|
|
|
|
} else {
|
2004-11-24 18:45:52 +00:00
|
|
|
bombout(("Strange packet received: type %d", pktin->type));
|
2003-04-23 13:48:09 +00:00
|
|
|
crStopV;
|
1999-01-08 13:02:13 +00:00
|
|
|
}
|
|
|
|
}
|
2018-05-18 06:22:58 +00:00
|
|
|
while (bufchain_size(&ssh->user_input) > 0) {
|
|
|
|
void *data;
|
|
|
|
int len;
|
|
|
|
bufchain_prefix(&ssh->user_input, &data, &len);
|
|
|
|
if (len > 512)
|
|
|
|
len = 512;
|
2018-06-09 08:59:58 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_STDIN_DATA);
|
|
|
|
put_string(pkt, data, len);
|
|
|
|
ssh_pkt_write(ssh, pkt);
|
2018-05-18 06:22:58 +00:00
|
|
|
bufchain_consume(&ssh->user_input, len);
|
|
|
|
}
|
|
|
|
crReturnV;
|
1999-01-08 13:02:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
crFinishV;
|
|
|
|
}
|
|
|
|
|
2004-11-24 20:35:15 +00:00
|
|
|
/*
|
2005-03-10 16:36:05 +00:00
|
|
|
* Handle the top-level SSH-2 protocol.
|
2004-11-24 20:35:15 +00:00
|
|
|
*/
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh1_msg_debug(Ssh ssh, PktIn *pktin)
|
2004-11-24 20:35:15 +00:00
|
|
|
{
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen msg = get_string(pktin);
|
|
|
|
logeventf(ssh, "Remote debug message: %.*s", PTRLEN_PRINTF(msg));
|
2004-11-24 20:35:15 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh1_msg_disconnect(Ssh ssh, PktIn *pktin)
|
2004-11-24 20:35:15 +00:00
|
|
|
{
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen msg = get_string(pktin);
|
|
|
|
bombout(("Server sent disconnect message:\n\"%.*s\"", PTRLEN_PRINTF(msg)));
|
2004-11-24 20:35:15 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh_msg_ignore(Ssh ssh, PktIn *pktin)
|
2004-11-24 20:35:15 +00:00
|
|
|
{
|
|
|
|
/* Do nothing, because we're ignoring it! Duhh. */
|
|
|
|
}
|
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
static void ssh1_login_input(Ssh ssh)
|
|
|
|
{
|
2018-05-18 06:22:58 +00:00
|
|
|
do_ssh1_login(ssh);
|
2018-05-18 06:22:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ssh1_connection_input(Ssh ssh)
|
|
|
|
{
|
2018-05-18 06:22:58 +00:00
|
|
|
do_ssh1_connection(ssh);
|
2018-05-18 06:22:58 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh1_coro_wrapper_initial(Ssh ssh, PktIn *pktin)
|
2018-05-18 06:22:57 +00:00
|
|
|
{
|
2018-05-18 06:22:58 +00:00
|
|
|
pktin->refcount++; /* avoid packet being freed when we return */
|
|
|
|
pq_push(&ssh->pq_ssh1_login, pktin);
|
|
|
|
queue_idempotent_callback(&ssh->ssh1_login_icb);
|
2018-05-18 06:22:57 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh1_coro_wrapper_session(Ssh ssh, PktIn *pktin)
|
2018-05-18 06:22:57 +00:00
|
|
|
{
|
2018-05-18 06:22:58 +00:00
|
|
|
pktin->refcount++; /* avoid packet being freed when we return */
|
|
|
|
pq_push(&ssh->pq_ssh1_connection, pktin);
|
|
|
|
queue_idempotent_callback(&ssh->ssh1_connection_icb);
|
2018-05-18 06:22:57 +00:00
|
|
|
}
|
|
|
|
|
2004-11-24 20:35:15 +00:00
|
|
|
static void ssh1_protocol_setup(Ssh ssh)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->bpp = ssh1_bpp_new();
|
|
|
|
|
2004-11-24 20:35:15 +00:00
|
|
|
/*
|
2018-05-18 06:22:57 +00:00
|
|
|
* Most messages are handled by the main protocol routine.
|
2004-11-24 20:35:15 +00:00
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
for (i = 0; i < SSH_MAX_MSG; i++)
|
2018-05-18 06:22:57 +00:00
|
|
|
ssh->packet_dispatch[i] = ssh1_coro_wrapper_initial;
|
2004-11-24 20:35:15 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* These special message types we install handlers for.
|
|
|
|
*/
|
|
|
|
ssh->packet_dispatch[SSH1_MSG_DISCONNECT] = ssh1_msg_disconnect;
|
|
|
|
ssh->packet_dispatch[SSH1_MSG_IGNORE] = ssh_msg_ignore;
|
|
|
|
ssh->packet_dispatch[SSH1_MSG_DEBUG] = ssh1_msg_debug;
|
|
|
|
}
|
|
|
|
|
2000-09-05 14:28:17 +00:00
|
|
|
/*
|
2015-05-17 22:14:57 +00:00
|
|
|
* Utility routines for decoding comma-separated strings in KEXINIT.
|
2005-01-22 13:32:41 +00:00
|
|
|
*/
|
2014-11-03 00:55:21 +00:00
|
|
|
static int first_in_commasep_string(char const *needle, char const *haystack,
|
|
|
|
int haylen)
|
2005-01-22 13:32:41 +00:00
|
|
|
{
|
|
|
|
int needlen;
|
|
|
|
if (!needle || !haystack) /* protect against null pointers */
|
|
|
|
return 0;
|
|
|
|
needlen = strlen(needle);
|
2015-05-17 22:14:57 +00:00
|
|
|
|
2005-01-22 13:32:41 +00:00
|
|
|
if (haylen >= needlen && /* haystack is long enough */
|
|
|
|
!memcmp(needle, haystack, needlen) && /* initial match */
|
|
|
|
(haylen == needlen || haystack[needlen] == ',')
|
|
|
|
/* either , or EOS follows */
|
|
|
|
)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-05-17 22:14:57 +00:00
|
|
|
static int in_commasep_string(char const *needle, char const *haystack,
|
|
|
|
int haylen)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
if (!needle || !haystack) /* protect against null pointers */
|
|
|
|
return 0;
|
|
|
|
/*
|
|
|
|
* Is it at the start of the string?
|
|
|
|
*/
|
|
|
|
if (first_in_commasep_string(needle, haystack, haylen))
|
|
|
|
return 1;
|
|
|
|
/*
|
|
|
|
* If not, search for the next comma and resume after that.
|
|
|
|
* If no comma found, terminate.
|
|
|
|
*/
|
|
|
|
p = memchr(haystack, ',', haylen);
|
|
|
|
if (!p) return 0;
|
|
|
|
/* + 1 to skip over comma */
|
|
|
|
return in_commasep_string(needle, p + 1, haylen - (p + 1 - haystack));
|
|
|
|
}
|
|
|
|
|
2014-11-03 00:55:21 +00:00
|
|
|
/*
|
2018-05-24 09:15:36 +00:00
|
|
|
* Add a value to a strbuf containing a comma-separated list.
|
2014-11-03 00:55:21 +00:00
|
|
|
*/
|
2018-05-24 09:15:36 +00:00
|
|
|
static void add_to_commasep(strbuf *buf, const char *data)
|
2014-11-03 00:55:21 +00:00
|
|
|
{
|
2018-05-24 09:15:36 +00:00
|
|
|
if (buf->len > 0)
|
|
|
|
put_byte(buf, ',');
|
|
|
|
put_data(buf, data, strlen(data));
|
2014-11-03 00:55:21 +00:00
|
|
|
}
|
|
|
|
|
2005-01-22 13:32:41 +00:00
|
|
|
|
2000-09-05 16:23:36 +00:00
|
|
|
/*
|
2015-08-21 22:40:16 +00:00
|
|
|
* SSH-2 key derivation (RFC 4253 section 7.2).
|
2000-09-05 16:23:36 +00:00
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
static void ssh2_mkkey(Ssh ssh, strbuf *out, Bignum K, unsigned char *H,
|
|
|
|
char chr, int keylen)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2018-09-13 15:41:46 +00:00
|
|
|
const struct ssh_hashalg *h = ssh->kex->hash;
|
2015-08-21 22:40:16 +00:00
|
|
|
int keylen_padded;
|
|
|
|
unsigned char *key;
|
2018-09-13 15:41:46 +00:00
|
|
|
ssh_hash *s, *s2;
|
2015-08-21 22:40:16 +00:00
|
|
|
|
|
|
|
if (keylen == 0)
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
return;
|
2015-08-21 22:40:16 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
/*
|
|
|
|
* Round the requested amount of key material up to a multiple of
|
|
|
|
* the length of the hash we're using to make it. This makes life
|
|
|
|
* simpler because then we can just write each hash output block
|
|
|
|
* straight into the output buffer without fiddling about
|
|
|
|
* truncating the last one. Since it's going into a strbuf, and
|
|
|
|
* strbufs are always smemclr()ed on free, there's no need to
|
|
|
|
* worry about leaving extra potentially-sensitive data in memory
|
|
|
|
* that the caller didn't ask for.
|
|
|
|
*/
|
2015-08-21 22:40:16 +00:00
|
|
|
keylen_padded = ((keylen + h->hlen - 1) / h->hlen) * h->hlen;
|
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
out->len = 0;
|
|
|
|
key = strbuf_append(out, keylen_padded);
|
2015-08-21 22:40:16 +00:00
|
|
|
|
2005-08-31 20:43:06 +00:00
|
|
|
/* First hlen bytes. */
|
2018-09-13 15:41:46 +00:00
|
|
|
s = ssh_hash_new(h);
|
2002-10-25 11:30:33 +00:00
|
|
|
if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))
|
2018-09-13 15:41:46 +00:00
|
|
|
put_mp_ssh2(s, K);
|
|
|
|
put_data(s, H, h->hlen);
|
|
|
|
put_byte(s, chr);
|
|
|
|
put_data(s, ssh->v2_session_id, ssh->v2_session_id_len);
|
|
|
|
ssh_hash_final(s, key);
|
2015-08-21 22:40:16 +00:00
|
|
|
|
|
|
|
/* Subsequent blocks of hlen bytes. */
|
|
|
|
if (keylen_padded > h->hlen) {
|
|
|
|
int offset;
|
|
|
|
|
2018-09-13 15:41:46 +00:00
|
|
|
s = ssh_hash_new(h);
|
2015-08-21 22:40:16 +00:00
|
|
|
if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))
|
2018-09-13 15:41:46 +00:00
|
|
|
put_mp_ssh2(s, K);
|
|
|
|
put_data(s, H, h->hlen);
|
2015-08-21 22:40:16 +00:00
|
|
|
|
|
|
|
for (offset = h->hlen; offset < keylen_padded; offset += h->hlen) {
|
2018-09-13 15:41:46 +00:00
|
|
|
put_data(s, key + offset - h->hlen, h->hlen);
|
|
|
|
s2 = ssh_hash_copy(s);
|
|
|
|
ssh_hash_final(s2, key + offset);
|
2015-08-21 22:40:16 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 15:41:46 +00:00
|
|
|
ssh_hash_free(s);
|
2015-08-21 22:40:16 +00:00
|
|
|
}
|
2000-09-05 16:23:36 +00:00
|
|
|
}
|
|
|
|
|
2015-05-30 08:10:48 +00:00
|
|
|
/*
|
|
|
|
* Structure for constructing KEXINIT algorithm lists.
|
|
|
|
*/
|
|
|
|
#define MAXKEXLIST 16
|
|
|
|
struct kexinit_algorithm {
|
|
|
|
const char *name;
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
const struct ssh_kex *kex;
|
|
|
|
int warn;
|
|
|
|
} kex;
|
2016-03-25 15:56:31 +00:00
|
|
|
struct {
|
Invent a struct type for polymorphic SSH key data.
During last week's work, I made a mistake in which I got the arguments
backwards in one of the key-blob-generating functions - mistakenly
swapped the 'void *' key instance with the 'BinarySink *' output
destination - and I didn't spot the mistake until run time, because in
C you can implicitly convert both to and from void * and so there was
no compile-time failure of type checking.
Now that I've introduced the FROMFIELD macro that downcasts a pointer
to one field of a structure to retrieve a pointer to the whole
structure, I think I might start using that more widely to indicate
this kind of polymorphic subtyping. So now all the public-key
functions in the struct ssh_signkey vtable handle their data instance
in the form of a pointer to a subfield of a new zero-sized structure
type 'ssh_key', which outside the key implementations indicates 'this
is some kind of key instance but it could be of any type'; they
downcast that pointer internally using FROMFIELD in place of the
previous ordinary C cast, and return one by returning &foo->sshk for
whatever foo they've just made up.
The sshk member is not at the beginning of the structure, which means
all those FROMFIELDs and &key->sshk are actually adding and
subtracting an offset. Of course I could have put the member at the
start anyway, but I had the idea that it's actually a feature _not_ to
have the two types start at the same address, because it means you
should notice earlier rather than later if you absentmindedly cast
from one to the other directly rather than by the approved method (in
particular, if you accidentally assign one through a void * and back
without even _noticing_ you perpetrated a cast). In particular, this
enforces that you can't sfree() the thing even once without realising
you should instead of called the right freekey function. (I found
several bugs by this method during initial testing, so I think it's
already proved its worth!)
While I'm here, I've also renamed the vtable structure ssh_signkey to
ssh_keyalg, because it was a confusing name anyway - it describes the
_algorithm_ for handling all keys of that type, not a specific key. So
ssh_keyalg is the collection of code, and ssh_key is one instance of
the data it handles.
2018-05-27 07:32:21 +00:00
|
|
|
const ssh_keyalg *hostkey;
|
2016-03-25 15:56:31 +00:00
|
|
|
int warn;
|
|
|
|
} hk;
|
2015-05-30 08:10:48 +00:00
|
|
|
struct {
|
2018-09-13 13:43:04 +00:00
|
|
|
const struct ssh2_cipheralg *cipher;
|
2015-05-30 08:10:48 +00:00
|
|
|
int warn;
|
|
|
|
} cipher;
|
|
|
|
struct {
|
2018-09-13 15:15:17 +00:00
|
|
|
const struct ssh2_macalg *mac;
|
2015-05-30 08:10:48 +00:00
|
|
|
int etm;
|
|
|
|
} mac;
|
2018-09-14 08:16:41 +00:00
|
|
|
const struct ssh_compression_alg *comp;
|
2015-05-30 08:10:48 +00:00
|
|
|
} u;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find a slot in a KEXINIT algorithm list to use for a new algorithm.
|
|
|
|
* If the algorithm is already in the list, return a pointer to its
|
|
|
|
* entry, otherwise return an entry from the end of the list.
|
|
|
|
* This assumes that every time a particular name is passed in, it
|
|
|
|
* comes from the same string constant. If this isn't true, this
|
|
|
|
* function may need to be rewritten to use strcmp() instead.
|
|
|
|
*/
|
|
|
|
static struct kexinit_algorithm *ssh2_kexinit_addalg(struct kexinit_algorithm
|
|
|
|
*list, const char *name)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < MAXKEXLIST; i++)
|
|
|
|
if (list[i].name == NULL || list[i].name == name) {
|
|
|
|
list[i].name = name;
|
|
|
|
return &list[i];
|
|
|
|
}
|
|
|
|
assert(!"No space in KEXINIT list");
|
2015-08-11 07:39:05 +00:00
|
|
|
return NULL;
|
2015-05-30 08:10:48 +00:00
|
|
|
}
|
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
#ifndef NO_GSSAPI
|
|
|
|
/*
|
|
|
|
* Data structure managing host keys in sessions based on GSSAPI KEX.
|
|
|
|
*
|
|
|
|
* In a session we started with a GSSAPI key exchange, the concept of
|
|
|
|
* 'host key' has completely different lifetime and security semantics
|
|
|
|
* from the usual ones. Per RFC 4462 section 2.1, we assume that any
|
|
|
|
* host key delivered to us in the course of a GSSAPI key exchange is
|
|
|
|
* _solely_ there to use as a transient fallback within the same
|
|
|
|
* session, if at the time of a subsequent rekey the GSS credentials
|
|
|
|
* are temporarily invalid and so a non-GSS KEX method has to be used.
|
|
|
|
*
|
|
|
|
* In particular, in a GSS-based SSH deployment, host keys may not
|
|
|
|
* even _be_ persistent identities for the server; it would be
|
|
|
|
* legitimate for a server to generate a fresh one routinely if it
|
|
|
|
* wanted to, like SSH-1 server keys.
|
|
|
|
*
|
|
|
|
* So, in this mode, we never touch the persistent host key cache at
|
|
|
|
* all, either to check keys against it _or_ to store keys in it.
|
|
|
|
* Instead, we maintain an in-memory cache of host keys that have been
|
|
|
|
* mentioned in GSS key exchanges within this particular session, and
|
|
|
|
* we permit precisely those host keys in non-GSS rekeys.
|
|
|
|
*/
|
|
|
|
struct ssh_transient_hostkey_cache_entry {
|
Invent a struct type for polymorphic SSH key data.
During last week's work, I made a mistake in which I got the arguments
backwards in one of the key-blob-generating functions - mistakenly
swapped the 'void *' key instance with the 'BinarySink *' output
destination - and I didn't spot the mistake until run time, because in
C you can implicitly convert both to and from void * and so there was
no compile-time failure of type checking.
Now that I've introduced the FROMFIELD macro that downcasts a pointer
to one field of a structure to retrieve a pointer to the whole
structure, I think I might start using that more widely to indicate
this kind of polymorphic subtyping. So now all the public-key
functions in the struct ssh_signkey vtable handle their data instance
in the form of a pointer to a subfield of a new zero-sized structure
type 'ssh_key', which outside the key implementations indicates 'this
is some kind of key instance but it could be of any type'; they
downcast that pointer internally using FROMFIELD in place of the
previous ordinary C cast, and return one by returning &foo->sshk for
whatever foo they've just made up.
The sshk member is not at the beginning of the structure, which means
all those FROMFIELDs and &key->sshk are actually adding and
subtracting an offset. Of course I could have put the member at the
start anyway, but I had the idea that it's actually a feature _not_ to
have the two types start at the same address, because it means you
should notice earlier rather than later if you absentmindedly cast
from one to the other directly rather than by the approved method (in
particular, if you accidentally assign one through a void * and back
without even _noticing_ you perpetrated a cast). In particular, this
enforces that you can't sfree() the thing even once without realising
you should instead of called the right freekey function. (I found
several bugs by this method during initial testing, so I think it's
already proved its worth!)
While I'm here, I've also renamed the vtable structure ssh_signkey to
ssh_keyalg, because it was a confusing name anyway - it describes the
_algorithm_ for handling all keys of that type, not a specific key. So
ssh_keyalg is the collection of code, and ssh_key is one instance of
the data it handles.
2018-05-27 07:32:21 +00:00
|
|
|
const ssh_keyalg *alg;
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf *pub_blob;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int ssh_transient_hostkey_cache_cmp(void *av, void *bv)
|
|
|
|
{
|
|
|
|
const struct ssh_transient_hostkey_cache_entry
|
|
|
|
*a = (const struct ssh_transient_hostkey_cache_entry *)av,
|
|
|
|
*b = (const struct ssh_transient_hostkey_cache_entry *)bv;
|
2018-06-03 11:58:05 +00:00
|
|
|
return strcmp(a->alg->ssh_id, b->alg->ssh_id);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ssh_transient_hostkey_cache_find(void *av, void *bv)
|
|
|
|
{
|
Invent a struct type for polymorphic SSH key data.
During last week's work, I made a mistake in which I got the arguments
backwards in one of the key-blob-generating functions - mistakenly
swapped the 'void *' key instance with the 'BinarySink *' output
destination - and I didn't spot the mistake until run time, because in
C you can implicitly convert both to and from void * and so there was
no compile-time failure of type checking.
Now that I've introduced the FROMFIELD macro that downcasts a pointer
to one field of a structure to retrieve a pointer to the whole
structure, I think I might start using that more widely to indicate
this kind of polymorphic subtyping. So now all the public-key
functions in the struct ssh_signkey vtable handle their data instance
in the form of a pointer to a subfield of a new zero-sized structure
type 'ssh_key', which outside the key implementations indicates 'this
is some kind of key instance but it could be of any type'; they
downcast that pointer internally using FROMFIELD in place of the
previous ordinary C cast, and return one by returning &foo->sshk for
whatever foo they've just made up.
The sshk member is not at the beginning of the structure, which means
all those FROMFIELDs and &key->sshk are actually adding and
subtracting an offset. Of course I could have put the member at the
start anyway, but I had the idea that it's actually a feature _not_ to
have the two types start at the same address, because it means you
should notice earlier rather than later if you absentmindedly cast
from one to the other directly rather than by the approved method (in
particular, if you accidentally assign one through a void * and back
without even _noticing_ you perpetrated a cast). In particular, this
enforces that you can't sfree() the thing even once without realising
you should instead of called the right freekey function. (I found
several bugs by this method during initial testing, so I think it's
already proved its worth!)
While I'm here, I've also renamed the vtable structure ssh_signkey to
ssh_keyalg, because it was a confusing name anyway - it describes the
_algorithm_ for handling all keys of that type, not a specific key. So
ssh_keyalg is the collection of code, and ssh_key is one instance of
the data it handles.
2018-05-27 07:32:21 +00:00
|
|
|
const ssh_keyalg *aalg = (const ssh_keyalg *)av;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
const struct ssh_transient_hostkey_cache_entry
|
|
|
|
*b = (const struct ssh_transient_hostkey_cache_entry *)bv;
|
2018-06-03 11:58:05 +00:00
|
|
|
return strcmp(aalg->ssh_id, b->alg->ssh_id);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ssh_init_transient_hostkey_store(Ssh ssh)
|
|
|
|
{
|
|
|
|
ssh->transient_hostkey_cache =
|
|
|
|
newtree234(ssh_transient_hostkey_cache_cmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ssh_cleanup_transient_hostkey_store(Ssh ssh)
|
|
|
|
{
|
|
|
|
struct ssh_transient_hostkey_cache_entry *ent;
|
|
|
|
while ((ent = delpos234(ssh->transient_hostkey_cache, 0)) != NULL) {
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf_free(ent->pub_blob);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
sfree(ent);
|
|
|
|
}
|
|
|
|
freetree234(ssh->transient_hostkey_cache);
|
|
|
|
}
|
|
|
|
|
2018-06-03 11:58:05 +00:00
|
|
|
static void ssh_store_transient_hostkey(Ssh ssh, ssh_key *key)
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
{
|
|
|
|
struct ssh_transient_hostkey_cache_entry *ent, *retd;
|
|
|
|
|
2018-06-03 11:58:05 +00:00
|
|
|
if ((ent = find234(ssh->transient_hostkey_cache, (void *)ssh_key_alg(key),
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
ssh_transient_hostkey_cache_find)) != NULL) {
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf_free(ent->pub_blob);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
sfree(ent);
|
|
|
|
}
|
|
|
|
|
|
|
|
ent = snew(struct ssh_transient_hostkey_cache_entry);
|
2018-06-03 11:58:05 +00:00
|
|
|
ent->alg = ssh_key_alg(key);
|
2018-05-24 09:59:39 +00:00
|
|
|
ent->pub_blob = strbuf_new();
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh_key_public_blob(key, BinarySink_UPCAST(ent->pub_blob));
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
retd = add234(ssh->transient_hostkey_cache, ent);
|
|
|
|
assert(retd == ent);
|
|
|
|
}
|
|
|
|
|
2018-06-03 11:58:05 +00:00
|
|
|
static int ssh_verify_transient_hostkey(Ssh ssh, ssh_key *key)
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
{
|
|
|
|
struct ssh_transient_hostkey_cache_entry *ent;
|
|
|
|
int toret = FALSE;
|
|
|
|
|
2018-06-03 11:58:05 +00:00
|
|
|
if ((ent = find234(ssh->transient_hostkey_cache, (void *)ssh_key_alg(key),
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
ssh_transient_hostkey_cache_find)) != NULL) {
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf *this_blob = strbuf_new();
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh_key_public_blob(key, BinarySink_UPCAST(this_blob));
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
|
2018-05-24 09:59:39 +00:00
|
|
|
if (this_blob->len == ent->pub_blob->len &&
|
|
|
|
!memcmp(this_blob->s, ent->pub_blob->s,
|
|
|
|
this_blob->len))
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
toret = TRUE;
|
|
|
|
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf_free(this_blob);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return toret;
|
|
|
|
}
|
|
|
|
|
Invent a struct type for polymorphic SSH key data.
During last week's work, I made a mistake in which I got the arguments
backwards in one of the key-blob-generating functions - mistakenly
swapped the 'void *' key instance with the 'BinarySink *' output
destination - and I didn't spot the mistake until run time, because in
C you can implicitly convert both to and from void * and so there was
no compile-time failure of type checking.
Now that I've introduced the FROMFIELD macro that downcasts a pointer
to one field of a structure to retrieve a pointer to the whole
structure, I think I might start using that more widely to indicate
this kind of polymorphic subtyping. So now all the public-key
functions in the struct ssh_signkey vtable handle their data instance
in the form of a pointer to a subfield of a new zero-sized structure
type 'ssh_key', which outside the key implementations indicates 'this
is some kind of key instance but it could be of any type'; they
downcast that pointer internally using FROMFIELD in place of the
previous ordinary C cast, and return one by returning &foo->sshk for
whatever foo they've just made up.
The sshk member is not at the beginning of the structure, which means
all those FROMFIELDs and &key->sshk are actually adding and
subtracting an offset. Of course I could have put the member at the
start anyway, but I had the idea that it's actually a feature _not_ to
have the two types start at the same address, because it means you
should notice earlier rather than later if you absentmindedly cast
from one to the other directly rather than by the approved method (in
particular, if you accidentally assign one through a void * and back
without even _noticing_ you perpetrated a cast). In particular, this
enforces that you can't sfree() the thing even once without realising
you should instead of called the right freekey function. (I found
several bugs by this method during initial testing, so I think it's
already proved its worth!)
While I'm here, I've also renamed the vtable structure ssh_signkey to
ssh_keyalg, because it was a confusing name anyway - it describes the
_algorithm_ for handling all keys of that type, not a specific key. So
ssh_keyalg is the collection of code, and ssh_key is one instance of
the data it handles.
2018-05-27 07:32:21 +00:00
|
|
|
static int ssh_have_transient_hostkey(Ssh ssh, const ssh_keyalg *alg)
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
{
|
|
|
|
struct ssh_transient_hostkey_cache_entry *ent =
|
|
|
|
find234(ssh->transient_hostkey_cache, (void *)alg,
|
|
|
|
ssh_transient_hostkey_cache_find);
|
|
|
|
return ent != NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ssh_have_any_transient_hostkey(Ssh ssh)
|
|
|
|
{
|
|
|
|
return count234(ssh->transient_hostkey_cache) > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* NO_GSSAPI */
|
|
|
|
|
2000-09-05 14:28:17 +00:00
|
|
|
/*
|
2005-03-10 16:36:05 +00:00
|
|
|
* Handle the SSH-2 transport layer.
|
2000-09-05 14:28:17 +00:00
|
|
|
*/
|
2018-05-18 06:22:58 +00:00
|
|
|
static void do_ssh2_transport(void *vctx)
|
2000-09-05 14:28:17 +00:00
|
|
|
{
|
2018-05-18 06:22:58 +00:00
|
|
|
Ssh ssh = (Ssh)vctx;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktIn *pktin;
|
2018-05-18 06:22:58 +00:00
|
|
|
|
2015-05-17 09:53:27 +00:00
|
|
|
enum kexlist {
|
|
|
|
KEXLIST_KEX, KEXLIST_HOSTKEY, KEXLIST_CSCIPHER, KEXLIST_SCCIPHER,
|
|
|
|
KEXLIST_CSMAC, KEXLIST_SCMAC, KEXLIST_CSCOMP, KEXLIST_SCCOMP,
|
|
|
|
NKEXLIST
|
|
|
|
};
|
|
|
|
const char * kexlist_descr[NKEXLIST] = {
|
|
|
|
"key exchange algorithm", "host key algorithm",
|
|
|
|
"client-to-server cipher", "server-to-client cipher",
|
|
|
|
"client-to-server MAC", "server-to-client MAC",
|
|
|
|
"client-to-server compression method",
|
|
|
|
"server-to-client compression method" };
|
2002-10-25 11:30:33 +00:00
|
|
|
struct do_ssh2_transport_state {
|
2012-08-18 09:30:01 +00:00
|
|
|
int crLine;
|
2016-03-25 15:56:31 +00:00
|
|
|
int nbits, pbits, warn_kex, warn_hk, warn_cscipher, warn_sccipher;
|
2002-10-25 11:30:33 +00:00
|
|
|
Bignum p, g, e, f, K;
|
2005-08-30 22:39:35 +00:00
|
|
|
void *our_kexinit;
|
|
|
|
int our_kexinitlen;
|
2002-10-25 11:30:33 +00:00
|
|
|
int kex_init_value, kex_reply_value;
|
2018-09-13 15:15:17 +00:00
|
|
|
const struct ssh2_macalg *const *maclist;
|
2002-10-25 11:30:33 +00:00
|
|
|
int nmacs;
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
struct {
|
2018-09-13 13:43:04 +00:00
|
|
|
const struct ssh2_cipheralg *cipher;
|
2018-09-13 15:15:17 +00:00
|
|
|
const struct ssh2_macalg *mac;
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
int etm_mode;
|
2018-09-14 08:16:41 +00:00
|
|
|
const struct ssh_compression_alg *comp;
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
} in, out;
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen hostkeydata, sigdata;
|
|
|
|
char *keystr, *fingerprint;
|
Invent a struct type for polymorphic SSH key data.
During last week's work, I made a mistake in which I got the arguments
backwards in one of the key-blob-generating functions - mistakenly
swapped the 'void *' key instance with the 'BinarySink *' output
destination - and I didn't spot the mistake until run time, because in
C you can implicitly convert both to and from void * and so there was
no compile-time failure of type checking.
Now that I've introduced the FROMFIELD macro that downcasts a pointer
to one field of a structure to retrieve a pointer to the whole
structure, I think I might start using that more widely to indicate
this kind of polymorphic subtyping. So now all the public-key
functions in the struct ssh_signkey vtable handle their data instance
in the form of a pointer to a subfield of a new zero-sized structure
type 'ssh_key', which outside the key implementations indicates 'this
is some kind of key instance but it could be of any type'; they
downcast that pointer internally using FROMFIELD in place of the
previous ordinary C cast, and return one by returning &foo->sshk for
whatever foo they've just made up.
The sshk member is not at the beginning of the structure, which means
all those FROMFIELDs and &key->sshk are actually adding and
subtracting an offset. Of course I could have put the member at the
start anyway, but I had the idea that it's actually a feature _not_ to
have the two types start at the same address, because it means you
should notice earlier rather than later if you absentmindedly cast
from one to the other directly rather than by the approved method (in
particular, if you accidentally assign one through a void * and back
without even _noticing_ you perpetrated a cast). In particular, this
enforces that you can't sfree() the thing even once without realising
you should instead of called the right freekey function. (I found
several bugs by this method during initial testing, so I think it's
already proved its worth!)
While I'm here, I've also renamed the vtable structure ssh_signkey to
ssh_keyalg, because it was a confusing name anyway - it describes the
_algorithm_ for handling all keys of that type, not a specific key. So
ssh_keyalg is the collection of code, and ssh_key is one instance of
the data it handles.
2018-05-27 07:32:21 +00:00
|
|
|
ssh_key *hkey; /* actual host key */
|
|
|
|
struct RSAKey *rsakey; /* for RSA kex */
|
|
|
|
struct ec_key *eckey; /* for ECDH kex */
|
2006-03-12 19:24:05 +00:00
|
|
|
unsigned char exchange_hash[SSH2_KEX_MAX_HASH_LEN];
|
2004-12-23 02:24:07 +00:00
|
|
|
int n_preferred_kex;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
int can_gssapi_keyex;
|
|
|
|
int need_gss_transient_hostkey;
|
|
|
|
int warned_about_no_gss_transient_hostkey;
|
2018-05-01 06:41:01 +00:00
|
|
|
const struct ssh_kexes *preferred_kex[KEX_MAX + 1]; /* +1 for GSSAPI */
|
2016-03-25 15:56:31 +00:00
|
|
|
int n_preferred_hk;
|
|
|
|
int preferred_hk[HK_MAX];
|
2002-10-25 11:30:33 +00:00
|
|
|
int n_preferred_ciphers;
|
|
|
|
const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX];
|
2018-09-14 08:16:41 +00:00
|
|
|
const struct ssh_compression_alg *preferred_comp;
|
2011-03-04 22:34:47 +00:00
|
|
|
int userauth_succeeded; /* for delayed compression */
|
|
|
|
int pending_compression;
|
2018-05-17 14:19:54 +00:00
|
|
|
int got_session_id;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
2005-02-17 18:34:24 +00:00
|
|
|
int dlgret;
|
|
|
|
int guessok;
|
2005-02-17 18:56:37 +00:00
|
|
|
int ignorepkt;
|
2015-05-30 08:10:48 +00:00
|
|
|
struct kexinit_algorithm kexlists[NKEXLIST][MAXKEXLIST];
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
#ifndef NO_GSSAPI
|
|
|
|
Ssh_gss_buf gss_buf;
|
|
|
|
Ssh_gss_buf gss_rcvtok, gss_sndtok;
|
|
|
|
Ssh_gss_stat gss_stat;
|
|
|
|
Ssh_gss_ctx gss_ctx;
|
|
|
|
Ssh_gss_buf mic;
|
|
|
|
int init_token_sent;
|
|
|
|
int complete_rcvd;
|
|
|
|
int gss_delegate;
|
|
|
|
time_t gss_cred_expiry;
|
|
|
|
#endif
|
2002-10-25 11:30:33 +00:00
|
|
|
};
|
|
|
|
crState(do_ssh2_transport_state);
|
|
|
|
|
2013-11-17 14:05:41 +00:00
|
|
|
assert(!ssh->bare_connection);
|
2016-05-01 17:16:22 +00:00
|
|
|
assert(ssh->version == 2);
|
2013-11-17 14:05:41 +00:00
|
|
|
|
2012-08-18 09:30:01 +00:00
|
|
|
crBeginState;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->in.cipher = s->out.cipher = NULL;
|
|
|
|
s->in.mac = s->out.mac = NULL;
|
|
|
|
s->in.comp = s->out.comp = NULL;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
2018-05-17 14:19:54 +00:00
|
|
|
s->got_session_id = FALSE;
|
2011-03-04 22:34:47 +00:00
|
|
|
s->userauth_succeeded = FALSE;
|
|
|
|
s->pending_compression = FALSE;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
s->need_gss_transient_hostkey = FALSE;
|
|
|
|
s->warned_about_no_gss_transient_hostkey = FALSE;
|
2000-09-05 14:28:17 +00:00
|
|
|
|
2004-12-31 10:51:14 +00:00
|
|
|
/*
|
|
|
|
* Be prepared to work around the buggy MAC problem.
|
|
|
|
*/
|
|
|
|
if (ssh->remote_bugs & BUG_SSH2_HMAC)
|
|
|
|
s->maclist = buggymacs, s->nmacs = lenof(buggymacs);
|
|
|
|
else
|
|
|
|
s->maclist = macs, s->nmacs = lenof(macs);
|
|
|
|
|
|
|
|
begin_key_exchange:
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
|
|
|
|
#ifndef NO_GSSAPI
|
|
|
|
if (s->need_gss_transient_hostkey) {
|
|
|
|
/*
|
|
|
|
* This flag indicates a special case in which we must not do
|
|
|
|
* GSS key exchange even if we could. (See comments below,
|
|
|
|
* where the flag was set on the previous key exchange.)
|
|
|
|
*/
|
|
|
|
s->can_gssapi_keyex = FALSE;
|
2018-04-26 18:15:15 +00:00
|
|
|
} else if (conf_get_int(ssh->conf, CONF_try_gssapi_kex)) {
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
/*
|
|
|
|
* We always check if we have GSS creds before we come up with
|
|
|
|
* the kex algorithm list, otherwise future rekeys will fail
|
|
|
|
* when creds expire. To make this so, this code section must
|
|
|
|
* follow the begin_key_exchange label above, otherwise this
|
|
|
|
* section would execute just once per-connection.
|
|
|
|
*
|
|
|
|
* Update GSS state unless the reason we're here is that a
|
|
|
|
* timer just checked the GSS state and decided that we should
|
|
|
|
* rekey to update delegated credentials. In that case, the
|
|
|
|
* state is "fresh".
|
|
|
|
*/
|
2018-05-18 06:22:58 +00:00
|
|
|
if (ssh->rekey_class != RK_GSS_UPDATE)
|
2018-05-01 17:54:04 +00:00
|
|
|
ssh2_gss_update(ssh, TRUE);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
|
|
|
|
/* Do GSSAPI KEX when capable */
|
|
|
|
s->can_gssapi_keyex = ssh->gss_status & GSS_KEX_CAPABLE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* But not when failure is likely. [ GSS implementations may
|
|
|
|
* attempt (and fail) to use a ticket that is almost expired
|
|
|
|
* when retrieved from the ccache that actually expires by the
|
|
|
|
* time the server receives it. ]
|
|
|
|
*
|
|
|
|
* Note: The first time always try KEXGSS if we can, failures
|
|
|
|
* will be very rare, and disabling the initial GSS KEX is
|
|
|
|
* worse. Some day GSS libraries will ignore cached tickets
|
|
|
|
* whose lifetime is critically short, and will instead use
|
|
|
|
* fresh ones.
|
|
|
|
*/
|
|
|
|
if (!s->got_session_id && (ssh->gss_status & GSS_CTXT_MAYFAIL) != 0)
|
|
|
|
s->can_gssapi_keyex = 0;
|
|
|
|
s->gss_delegate = conf_get_int(ssh->conf, CONF_gssapifwd);
|
2018-04-26 18:15:15 +00:00
|
|
|
} else {
|
|
|
|
s->can_gssapi_keyex = FALSE;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->pls.kctx = SSH2_PKTCTX_NOKEX;
|
2004-12-23 02:24:07 +00:00
|
|
|
{
|
2015-05-30 08:10:48 +00:00
|
|
|
int i, j, k, warn;
|
|
|
|
struct kexinit_algorithm *alg;
|
2004-12-31 10:51:14 +00:00
|
|
|
|
2004-12-23 02:24:07 +00:00
|
|
|
/*
|
|
|
|
* Set up the preferred key exchange. (NULL => warn below here)
|
|
|
|
*/
|
|
|
|
s->n_preferred_kex = 0;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
if (s->can_gssapi_keyex)
|
|
|
|
s->preferred_kex[s->n_preferred_kex++] = &ssh_gssk5_sha1_kex;
|
2018-05-01 06:41:01 +00:00
|
|
|
for (i = 0; i < KEX_MAX; i++) {
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
switch (conf_get_int_int(ssh->conf, CONF_ssh_kexlist, i)) {
|
2004-12-23 02:24:07 +00:00
|
|
|
case KEX_DHGEX:
|
|
|
|
s->preferred_kex[s->n_preferred_kex++] =
|
|
|
|
&ssh_diffiehellman_gex;
|
|
|
|
break;
|
|
|
|
case KEX_DHGROUP14:
|
|
|
|
s->preferred_kex[s->n_preferred_kex++] =
|
|
|
|
&ssh_diffiehellman_group14;
|
|
|
|
break;
|
|
|
|
case KEX_DHGROUP1:
|
|
|
|
s->preferred_kex[s->n_preferred_kex++] =
|
|
|
|
&ssh_diffiehellman_group1;
|
|
|
|
break;
|
2007-04-30 22:09:26 +00:00
|
|
|
case KEX_RSA:
|
|
|
|
s->preferred_kex[s->n_preferred_kex++] =
|
|
|
|
&ssh_rsa_kex;
|
|
|
|
break;
|
2014-11-01 09:45:20 +00:00
|
|
|
case KEX_ECDH:
|
|
|
|
s->preferred_kex[s->n_preferred_kex++] =
|
|
|
|
&ssh_ecdh_kex;
|
|
|
|
break;
|
2006-03-12 19:24:05 +00:00
|
|
|
case KEX_WARN:
|
2004-12-23 02:24:07 +00:00
|
|
|
/* Flag for later. Don't bother if it's the last in
|
|
|
|
* the list. */
|
|
|
|
if (i < KEX_MAX - 1) {
|
|
|
|
s->preferred_kex[s->n_preferred_kex++] = NULL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-25 15:56:31 +00:00
|
|
|
/*
|
|
|
|
* Set up the preferred host key types. These are just the ids
|
|
|
|
* in the enum in putty.h, so 'warn below here' is indicated
|
|
|
|
* by HK_WARN.
|
|
|
|
*/
|
|
|
|
s->n_preferred_hk = 0;
|
|
|
|
for (i = 0; i < HK_MAX; i++) {
|
|
|
|
int id = conf_get_int_int(ssh->conf, CONF_ssh_hklist, i);
|
|
|
|
/* As above, don't bother with HK_WARN if it's last in the
|
|
|
|
* list */
|
|
|
|
if (id != HK_WARN || i < HK_MAX - 1)
|
|
|
|
s->preferred_hk[s->n_preferred_hk++] = id;
|
|
|
|
}
|
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
/*
|
|
|
|
* Set up the preferred ciphers. (NULL => warn below here)
|
|
|
|
*/
|
|
|
|
s->n_preferred_ciphers = 0;
|
|
|
|
for (i = 0; i < CIPHER_MAX; i++) {
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
switch (conf_get_int_int(ssh->conf, CONF_ssh_cipherlist, i)) {
|
2002-10-25 11:30:33 +00:00
|
|
|
case CIPHER_BLOWFISH:
|
|
|
|
s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_blowfish;
|
|
|
|
break;
|
|
|
|
case CIPHER_DES:
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (conf_get_int(ssh->conf, CONF_ssh2_des_cbc)) {
|
2002-10-25 11:30:33 +00:00
|
|
|
s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_des;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CIPHER_3DES:
|
|
|
|
s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_3des;
|
|
|
|
break;
|
|
|
|
case CIPHER_AES:
|
|
|
|
s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_aes;
|
|
|
|
break;
|
2005-04-14 22:58:29 +00:00
|
|
|
case CIPHER_ARCFOUR:
|
|
|
|
s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_arcfour;
|
|
|
|
break;
|
2015-06-07 11:51:51 +00:00
|
|
|
case CIPHER_CHACHA20:
|
|
|
|
s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_ccp;
|
|
|
|
break;
|
2002-10-25 11:30:33 +00:00
|
|
|
case CIPHER_WARN:
|
|
|
|
/* Flag for later. Don't bother if it's the last in
|
|
|
|
* the list. */
|
|
|
|
if (i < CIPHER_MAX - 1) {
|
|
|
|
s->preferred_ciphers[s->n_preferred_ciphers++] = NULL;
|
|
|
|
}
|
|
|
|
break;
|
2001-08-25 19:33:33 +00:00
|
|
|
}
|
|
|
|
}
|
2000-10-12 12:39:44 +00:00
|
|
|
|
2004-12-31 10:51:14 +00:00
|
|
|
/*
|
|
|
|
* Set up preferred compression.
|
|
|
|
*/
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (conf_get_int(ssh->conf, CONF_compression))
|
2004-12-31 10:51:14 +00:00
|
|
|
s->preferred_comp = &ssh_zlib;
|
|
|
|
else
|
|
|
|
s->preferred_comp = &ssh_comp_none;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
2004-11-24 19:23:02 +00:00
|
|
|
/*
|
|
|
|
* Enable queueing of outgoing auth- or connection-layer
|
|
|
|
* packets while we are in the middle of a key exchange.
|
|
|
|
*/
|
|
|
|
ssh->queueing = TRUE;
|
|
|
|
|
2004-11-27 14:29:20 +00:00
|
|
|
/*
|
|
|
|
* Flag that KEX is in progress.
|
|
|
|
*/
|
|
|
|
ssh->kex_in_progress = TRUE;
|
|
|
|
|
2015-05-17 09:53:27 +00:00
|
|
|
for (i = 0; i < NKEXLIST; i++)
|
|
|
|
for (j = 0; j < MAXKEXLIST; j++)
|
|
|
|
s->kexlists[i][j].name = NULL;
|
2002-10-25 11:30:33 +00:00
|
|
|
/* List key exchange algorithms. */
|
2015-05-17 09:53:27 +00:00
|
|
|
warn = FALSE;
|
2004-12-23 02:24:07 +00:00
|
|
|
for (i = 0; i < s->n_preferred_kex; i++) {
|
2005-09-03 13:41:43 +00:00
|
|
|
const struct ssh_kexes *k = s->preferred_kex[i];
|
2015-05-17 09:53:27 +00:00
|
|
|
if (!k) warn = TRUE;
|
|
|
|
else for (j = 0; j < k->nkexes; j++) {
|
2015-05-30 08:10:48 +00:00
|
|
|
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_KEX],
|
|
|
|
k->list[j]->name);
|
|
|
|
alg->u.kex.kex = k->list[j];
|
|
|
|
alg->u.kex.warn = warn;
|
2015-05-17 09:53:27 +00:00
|
|
|
}
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
2002-10-25 11:30:33 +00:00
|
|
|
/* List server host key algorithms. */
|
2013-08-18 06:48:20 +00:00
|
|
|
if (!s->got_session_id) {
|
|
|
|
/*
|
|
|
|
* In the first key exchange, we list all the algorithms
|
2015-05-29 21:40:50 +00:00
|
|
|
* we're prepared to cope with, but prefer those algorithms
|
|
|
|
* for which we have a host key for this host.
|
2016-03-25 15:56:31 +00:00
|
|
|
*
|
|
|
|
* If the host key algorithm is below the warning
|
|
|
|
* threshold, we warn even if we did already have a key
|
|
|
|
* for it, on the basis that if the user has just
|
|
|
|
* reconfigured that host key type to be warned about,
|
|
|
|
* they surely _do_ want to be alerted that a server
|
|
|
|
* they're actually connecting to is using it.
|
2013-08-18 06:48:20 +00:00
|
|
|
*/
|
2016-03-25 15:56:31 +00:00
|
|
|
warn = FALSE;
|
|
|
|
for (i = 0; i < s->n_preferred_hk; i++) {
|
|
|
|
if (s->preferred_hk[i] == HK_WARN)
|
|
|
|
warn = TRUE;
|
|
|
|
for (j = 0; j < lenof(hostkey_algs); j++) {
|
|
|
|
if (hostkey_algs[j].id != s->preferred_hk[i])
|
|
|
|
continue;
|
|
|
|
if (have_ssh_host_key(ssh->savedhost, ssh->savedport,
|
2018-06-03 11:58:05 +00:00
|
|
|
hostkey_algs[j].alg->cache_id)) {
|
2016-03-25 15:56:31 +00:00
|
|
|
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
|
2018-06-03 11:58:05 +00:00
|
|
|
hostkey_algs[j].alg->ssh_id);
|
2016-03-25 15:56:31 +00:00
|
|
|
alg->u.hk.hostkey = hostkey_algs[j].alg;
|
|
|
|
alg->u.hk.warn = warn;
|
|
|
|
}
|
|
|
|
}
|
2015-05-17 09:53:27 +00:00
|
|
|
}
|
2016-03-25 15:56:31 +00:00
|
|
|
warn = FALSE;
|
|
|
|
for (i = 0; i < s->n_preferred_hk; i++) {
|
|
|
|
if (s->preferred_hk[i] == HK_WARN)
|
|
|
|
warn = TRUE;
|
|
|
|
for (j = 0; j < lenof(hostkey_algs); j++) {
|
|
|
|
if (hostkey_algs[j].id != s->preferred_hk[i])
|
|
|
|
continue;
|
|
|
|
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
|
2018-06-03 11:58:05 +00:00
|
|
|
hostkey_algs[j].alg->ssh_id);
|
2016-03-25 15:56:31 +00:00
|
|
|
alg->u.hk.hostkey = hostkey_algs[j].alg;
|
|
|
|
alg->u.hk.warn = warn;
|
|
|
|
}
|
|
|
|
}
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
#ifndef NO_GSSAPI
|
|
|
|
} else if (ssh->gss_kex_used && !s->need_gss_transient_hostkey) {
|
|
|
|
/*
|
|
|
|
* If we've previously done a GSSAPI KEX, then we list
|
|
|
|
* precisely the algorithms for which a previous GSS key
|
|
|
|
* exchange has delivered us a host key, because we expect
|
|
|
|
* one of exactly those keys to be used in any subsequent
|
|
|
|
* non-GSS-based rekey.
|
|
|
|
*
|
|
|
|
* An exception is if this is the key exchange we
|
|
|
|
* triggered for the purposes of populating that cache -
|
|
|
|
* in which case the cache will currently be empty, which
|
|
|
|
* isn't helpful!
|
|
|
|
*/
|
|
|
|
warn = FALSE;
|
|
|
|
for (i = 0; i < s->n_preferred_hk; i++) {
|
|
|
|
if (s->preferred_hk[i] == HK_WARN)
|
|
|
|
warn = TRUE;
|
|
|
|
for (j = 0; j < lenof(hostkey_algs); j++) {
|
|
|
|
if (hostkey_algs[j].id != s->preferred_hk[i])
|
|
|
|
continue;
|
|
|
|
if (ssh_have_transient_hostkey(ssh, hostkey_algs[j].alg)) {
|
|
|
|
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
|
2018-06-03 11:58:05 +00:00
|
|
|
hostkey_algs[j].alg->ssh_id);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
alg->u.hk.hostkey = hostkey_algs[j].alg;
|
|
|
|
alg->u.hk.warn = warn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2013-08-18 06:48:20 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* In subsequent key exchanges, we list only the kex
|
|
|
|
* algorithm that was selected in the first key exchange,
|
|
|
|
* so that we keep getting the same host key and hence
|
|
|
|
* don't have to interrupt the user's session to ask for
|
|
|
|
* reverification.
|
|
|
|
*/
|
|
|
|
assert(ssh->kex);
|
2015-05-30 08:10:48 +00:00
|
|
|
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh->hostkey_alg->ssh_id);
|
|
|
|
alg->u.hk.hostkey = ssh->hostkey_alg;
|
2016-03-25 15:56:31 +00:00
|
|
|
alg->u.hk.warn = FALSE;
|
2013-08-18 06:48:20 +00:00
|
|
|
}
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
if (s->can_gssapi_keyex) {
|
|
|
|
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY], "null");
|
|
|
|
alg->u.hk.hostkey = NULL;
|
|
|
|
}
|
2012-08-21 22:33:31 +00:00
|
|
|
/* List encryption algorithms (client->server then server->client). */
|
2015-05-17 09:53:27 +00:00
|
|
|
for (k = KEXLIST_CSCIPHER; k <= KEXLIST_SCCIPHER; k++) {
|
|
|
|
warn = FALSE;
|
2015-10-18 12:04:58 +00:00
|
|
|
#ifdef FUZZING
|
|
|
|
alg = ssh2_kexinit_addalg(s->kexlists[k], "none");
|
|
|
|
alg->u.cipher.cipher = NULL;
|
|
|
|
alg->u.cipher.warn = warn;
|
|
|
|
#endif /* FUZZING */
|
2012-08-21 22:33:31 +00:00
|
|
|
for (i = 0; i < s->n_preferred_ciphers; i++) {
|
|
|
|
const struct ssh2_ciphers *c = s->preferred_ciphers[i];
|
2015-05-17 09:53:27 +00:00
|
|
|
if (!c) warn = TRUE;
|
|
|
|
else for (j = 0; j < c->nciphers; j++) {
|
2015-05-30 08:10:48 +00:00
|
|
|
alg = ssh2_kexinit_addalg(s->kexlists[k],
|
|
|
|
c->list[j]->name);
|
|
|
|
alg->u.cipher.cipher = c->list[j];
|
|
|
|
alg->u.cipher.warn = warn;
|
2015-05-17 09:53:27 +00:00
|
|
|
}
|
2002-10-25 11:30:33 +00:00
|
|
|
}
|
|
|
|
}
|
2012-08-21 22:33:31 +00:00
|
|
|
/* List MAC algorithms (client->server then server->client). */
|
2015-05-17 09:53:27 +00:00
|
|
|
for (j = KEXLIST_CSMAC; j <= KEXLIST_SCMAC; j++) {
|
2015-10-18 12:04:58 +00:00
|
|
|
#ifdef FUZZING
|
|
|
|
alg = ssh2_kexinit_addalg(s->kexlists[j], "none");
|
|
|
|
alg->u.mac.mac = NULL;
|
|
|
|
alg->u.mac.etm = FALSE;
|
|
|
|
#endif /* FUZZING */
|
2015-04-26 22:30:32 +00:00
|
|
|
for (i = 0; i < s->nmacs; i++) {
|
2015-05-30 08:10:48 +00:00
|
|
|
alg = ssh2_kexinit_addalg(s->kexlists[j], s->maclist[i]->name);
|
|
|
|
alg->u.mac.mac = s->maclist[i];
|
|
|
|
alg->u.mac.etm = FALSE;
|
2015-04-26 22:30:32 +00:00
|
|
|
}
|
2015-05-17 09:53:27 +00:00
|
|
|
for (i = 0; i < s->nmacs; i++)
|
2015-04-26 22:30:32 +00:00
|
|
|
/* For each MAC, there may also be an ETM version,
|
|
|
|
* which we list second. */
|
2015-05-17 09:53:27 +00:00
|
|
|
if (s->maclist[i]->etm_name) {
|
2015-05-30 08:10:48 +00:00
|
|
|
alg = ssh2_kexinit_addalg(s->kexlists[j],
|
|
|
|
s->maclist[i]->etm_name);
|
|
|
|
alg->u.mac.mac = s->maclist[i];
|
|
|
|
alg->u.mac.etm = TRUE;
|
2015-05-17 09:53:27 +00:00
|
|
|
}
|
2002-10-25 11:30:33 +00:00
|
|
|
}
|
2011-03-04 22:34:47 +00:00
|
|
|
/* List client->server compression algorithms,
|
|
|
|
* then server->client compression algorithms. (We use the
|
|
|
|
* same set twice.) */
|
2015-05-17 09:53:27 +00:00
|
|
|
for (j = KEXLIST_CSCOMP; j <= KEXLIST_SCCOMP; j++) {
|
2011-03-04 22:34:47 +00:00
|
|
|
assert(lenof(compressions) > 1);
|
|
|
|
/* Prefer non-delayed versions */
|
2015-05-30 08:10:48 +00:00
|
|
|
alg = ssh2_kexinit_addalg(s->kexlists[j], s->preferred_comp->name);
|
|
|
|
alg->u.comp = s->preferred_comp;
|
2011-03-04 22:34:47 +00:00
|
|
|
/* We don't even list delayed versions of algorithms until
|
|
|
|
* they're allowed to be used, to avoid a race. See the end of
|
|
|
|
* this function. */
|
2015-05-17 09:53:27 +00:00
|
|
|
if (s->userauth_succeeded && s->preferred_comp->delayed_name) {
|
2015-05-30 08:10:48 +00:00
|
|
|
alg = ssh2_kexinit_addalg(s->kexlists[j],
|
|
|
|
s->preferred_comp->delayed_name);
|
|
|
|
alg->u.comp = s->preferred_comp;
|
2015-05-17 09:53:27 +00:00
|
|
|
}
|
2011-03-04 22:34:47 +00:00
|
|
|
for (i = 0; i < lenof(compressions); i++) {
|
2018-09-14 08:16:41 +00:00
|
|
|
const struct ssh_compression_alg *c = compressions[i];
|
2015-05-30 08:10:48 +00:00
|
|
|
alg = ssh2_kexinit_addalg(s->kexlists[j], c->name);
|
|
|
|
alg->u.comp = c;
|
2015-05-17 09:53:27 +00:00
|
|
|
if (s->userauth_succeeded && c->delayed_name) {
|
2015-05-30 08:10:48 +00:00
|
|
|
alg = ssh2_kexinit_addalg(s->kexlists[j], c->delayed_name);
|
|
|
|
alg->u.comp = c;
|
2015-05-17 09:53:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Construct and send our key exchange packet.
|
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_KEXINIT);
|
2015-05-17 09:53:27 +00:00
|
|
|
for (i = 0; i < 16; i++)
|
2018-05-24 09:15:36 +00:00
|
|
|
put_byte(s->pktout, (unsigned char) random_byte());
|
2015-05-17 09:53:27 +00:00
|
|
|
for (i = 0; i < NKEXLIST; i++) {
|
2018-05-24 09:15:36 +00:00
|
|
|
strbuf *list = strbuf_new();
|
2015-05-17 09:53:27 +00:00
|
|
|
for (j = 0; j < MAXKEXLIST; j++) {
|
|
|
|
if (s->kexlists[i][j].name == NULL) break;
|
2018-05-24 09:15:36 +00:00
|
|
|
add_to_commasep(list, s->kexlists[i][j].name);
|
2004-09-03 12:28:19 +00:00
|
|
|
}
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringsb(s->pktout, list);
|
2002-10-25 11:30:33 +00:00
|
|
|
}
|
|
|
|
/* List client->server languages. Empty list. */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, "");
|
2002-10-25 11:30:33 +00:00
|
|
|
/* List server->client languages. Empty list. */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, "");
|
2002-10-25 11:30:33 +00:00
|
|
|
/* First KEX packet does _not_ follow, because we're not that brave. */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_bool(s->pktout, FALSE);
|
2002-10-25 11:30:33 +00:00
|
|
|
/* Reserved. */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(s->pktout, 0);
|
2000-09-05 14:28:17 +00:00
|
|
|
}
|
2000-12-18 09:20:08 +00:00
|
|
|
|
2005-08-30 22:39:35 +00:00
|
|
|
s->our_kexinitlen = s->pktout->length - 5;
|
|
|
|
s->our_kexinit = snewn(s->our_kexinitlen, unsigned char);
|
|
|
|
memcpy(s->our_kexinit, s->pktout->data + 5, s->our_kexinitlen);
|
2000-12-18 09:20:08 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh_pkt_write(ssh, s->pktout);
|
2000-09-05 14:28:17 +00:00
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL);
|
2000-09-05 14:28:17 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now examine the other side's KEXINIT to see what we're up
|
|
|
|
* to.
|
|
|
|
*/
|
2002-10-25 11:30:33 +00:00
|
|
|
{
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen str;
|
|
|
|
int i, j;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
2004-11-24 18:45:52 +00:00
|
|
|
if (pktin->type != SSH2_MSG_KEXINIT) {
|
2003-03-31 11:22:30 +00:00
|
|
|
bombout(("expected key exchange packet from server"));
|
2012-08-27 22:02:17 +00:00
|
|
|
crStopV;
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->kex = NULL;
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh->hostkey_alg = NULL;
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->in.cipher = s->out.cipher = NULL;
|
|
|
|
s->in.mac = s->out.mac = NULL;
|
|
|
|
s->in.comp = s->out.comp = NULL;
|
2016-03-25 15:56:31 +00:00
|
|
|
s->warn_kex = s->warn_hk = FALSE;
|
|
|
|
s->warn_cscipher = s->warn_sccipher = FALSE;
|
2005-02-17 18:56:37 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
get_data(pktin, 16); /* skip garbage cookie */
|
2005-02-17 18:56:37 +00:00
|
|
|
|
2015-05-17 09:53:27 +00:00
|
|
|
s->guessok = FALSE;
|
|
|
|
for (i = 0; i < NKEXLIST; i++) {
|
2018-05-27 17:13:53 +00:00
|
|
|
str = get_string(pktin);
|
|
|
|
if (get_err(pktin)) {
|
2015-05-17 09:53:27 +00:00
|
|
|
bombout(("KEXINIT packet was incomplete"));
|
|
|
|
crStopV;
|
2002-10-25 11:30:33 +00:00
|
|
|
}
|
2015-09-24 10:41:43 +00:00
|
|
|
|
|
|
|
/* If we've already selected a cipher which requires a
|
|
|
|
* particular MAC, then just select that, and don't even
|
|
|
|
* bother looking through the server's KEXINIT string for
|
|
|
|
* MACs. */
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
if (i == KEXLIST_CSMAC && s->out.cipher &&
|
|
|
|
s->out.cipher->required_mac) {
|
|
|
|
s->out.mac = s->out.cipher->required_mac;
|
|
|
|
s->out.etm_mode = !!(s->out.mac->etm_name);
|
2015-09-24 10:41:43 +00:00
|
|
|
goto matched;
|
|
|
|
}
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
if (i == KEXLIST_SCMAC && s->in.cipher &&
|
|
|
|
s->in.cipher->required_mac) {
|
|
|
|
s->in.mac = s->in.cipher->required_mac;
|
|
|
|
s->in.etm_mode = !!(s->in.mac->etm_name);
|
2015-09-24 10:41:43 +00:00
|
|
|
goto matched;
|
|
|
|
}
|
|
|
|
|
2015-05-17 09:53:27 +00:00
|
|
|
for (j = 0; j < MAXKEXLIST; j++) {
|
|
|
|
struct kexinit_algorithm *alg = &s->kexlists[i][j];
|
|
|
|
if (alg->name == NULL) break;
|
2018-05-27 17:13:53 +00:00
|
|
|
if (in_commasep_string(alg->name, str.ptr, str.len)) {
|
2015-05-17 09:53:27 +00:00
|
|
|
/* We've found a matching algorithm. */
|
|
|
|
if (i == KEXLIST_KEX || i == KEXLIST_HOSTKEY) {
|
|
|
|
/* Check if we might need to ignore first kex pkt */
|
|
|
|
if (j != 0 ||
|
2018-05-27 17:13:53 +00:00
|
|
|
!first_in_commasep_string(alg->name,
|
|
|
|
str.ptr, str.len))
|
2015-05-17 09:53:27 +00:00
|
|
|
s->guessok = FALSE;
|
2002-10-25 11:30:33 +00:00
|
|
|
}
|
2015-05-17 09:53:27 +00:00
|
|
|
if (i == KEXLIST_KEX) {
|
|
|
|
ssh->kex = alg->u.kex.kex;
|
|
|
|
s->warn_kex = alg->u.kex.warn;
|
|
|
|
} else if (i == KEXLIST_HOSTKEY) {
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
/*
|
|
|
|
* Ignore an unexpected/inappropriate offer of "null",
|
|
|
|
* we offer "null" when we're willing to use GSS KEX,
|
|
|
|
* but it is only acceptable when GSSKEX is actually
|
|
|
|
* selected.
|
|
|
|
*/
|
|
|
|
if (alg->u.hk.hostkey == NULL &&
|
|
|
|
ssh->kex->main_type != KEXTYPE_GSS)
|
|
|
|
continue;
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh->hostkey_alg = alg->u.hk.hostkey;
|
2016-03-25 15:56:31 +00:00
|
|
|
s->warn_hk = alg->u.hk.warn;
|
2015-05-17 09:53:27 +00:00
|
|
|
} else if (i == KEXLIST_CSCIPHER) {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->out.cipher = alg->u.cipher.cipher;
|
2015-05-17 09:53:27 +00:00
|
|
|
s->warn_cscipher = alg->u.cipher.warn;
|
|
|
|
} else if (i == KEXLIST_SCCIPHER) {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->in.cipher = alg->u.cipher.cipher;
|
2015-05-17 09:53:27 +00:00
|
|
|
s->warn_sccipher = alg->u.cipher.warn;
|
|
|
|
} else if (i == KEXLIST_CSMAC) {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->out.mac = alg->u.mac.mac;
|
|
|
|
s->out.etm_mode = alg->u.mac.etm;
|
2015-05-17 09:53:27 +00:00
|
|
|
} else if (i == KEXLIST_SCMAC) {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->in.mac = alg->u.mac.mac;
|
|
|
|
s->in.etm_mode = alg->u.mac.etm;
|
2015-05-17 09:53:27 +00:00
|
|
|
} else if (i == KEXLIST_CSCOMP) {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->out.comp = alg->u.comp;
|
2015-05-17 09:53:27 +00:00
|
|
|
} else if (i == KEXLIST_SCCOMP) {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->in.comp = alg->u.comp;
|
2002-10-25 11:30:33 +00:00
|
|
|
}
|
2015-05-17 09:53:27 +00:00
|
|
|
goto matched;
|
2001-08-25 19:33:33 +00:00
|
|
|
}
|
2015-05-17 09:53:27 +00:00
|
|
|
if ((i == KEXLIST_CSCOMP || i == KEXLIST_SCCOMP) &&
|
2018-05-27 17:13:53 +00:00
|
|
|
in_commasep_string(alg->u.comp->delayed_name,
|
2018-06-16 13:44:10 +00:00
|
|
|
str.ptr, str.len) &&
|
|
|
|
!s->userauth_succeeded)
|
2011-03-04 22:34:47 +00:00
|
|
|
s->pending_compression = TRUE; /* try this later */
|
2002-10-25 11:30:33 +00:00
|
|
|
}
|
2016-03-25 15:37:16 +00:00
|
|
|
bombout(("Couldn't agree a %s (available: %.*s)",
|
2018-05-27 17:13:53 +00:00
|
|
|
kexlist_descr[i], PTRLEN_PRINTF(str)));
|
2015-05-17 09:53:27 +00:00
|
|
|
crStopV;
|
|
|
|
matched:;
|
Add manual cross-certification of new host keys.
If a server offers host key algorithms that we don't have a stored key
for, they will now appear in a submenu of the Special Commands menu.
Selecting one will force a repeat key exchange with that key, and if
it succeeds, will add the new host key to the cache. The idea is that
the new key sent by the server is protected by the crypto established
in the previous key exchange, so this is just as safe as typing some
command like 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub' at the
server prompt and transcribing the results manually.
This allows switching over to newer host key algorithms if the client
has begun to support them (e.g. people using PuTTY's new ECC
functionality for the first time), or if the server has acquired a new
key (e.g. due to a server OS upgrade).
At the moment, it's only available manually, for a single host key
type at a time. Automating it is potentially controversial for
security policy reasons (what if someone doesn't agree this is what
they want in their host key cache, or doesn't want to switch over to
using whichever of the keys PuTTY would now put top of the list?), for
code plumbing reasons (chaining several of these rekeys might be more
annoying than doing one at a time) and for CPU usage reasons (rekeys
are expensive), but even so, it might turn out to be a good idea in
future.
2016-03-21 07:25:31 +00:00
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
if (i == KEXLIST_HOSTKEY &&
|
|
|
|
!ssh->gss_kex_used &&
|
|
|
|
ssh->kex->main_type != KEXTYPE_GSS) {
|
Add manual cross-certification of new host keys.
If a server offers host key algorithms that we don't have a stored key
for, they will now appear in a submenu of the Special Commands menu.
Selecting one will force a repeat key exchange with that key, and if
it succeeds, will add the new host key to the cache. The idea is that
the new key sent by the server is protected by the crypto established
in the previous key exchange, so this is just as safe as typing some
command like 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub' at the
server prompt and transcribing the results manually.
This allows switching over to newer host key algorithms if the client
has begun to support them (e.g. people using PuTTY's new ECC
functionality for the first time), or if the server has acquired a new
key (e.g. due to a server OS upgrade).
At the moment, it's only available manually, for a single host key
type at a time. Automating it is potentially controversial for
security policy reasons (what if someone doesn't agree this is what
they want in their host key cache, or doesn't want to switch over to
using whichever of the keys PuTTY would now put top of the list?), for
code plumbing reasons (chaining several of these rekeys might be more
annoying than doing one at a time) and for CPU usage reasons (rekeys
are expensive), but even so, it might turn out to be a good idea in
future.
2016-03-21 07:25:31 +00:00
|
|
|
int j;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In addition to deciding which host key we're
|
|
|
|
* actually going to use, we should make a list of the
|
|
|
|
* host keys offered by the server which we _don't_
|
|
|
|
* have cached. These will be offered as cross-
|
|
|
|
* certification options by ssh_get_specials.
|
2016-03-21 19:05:32 +00:00
|
|
|
*
|
|
|
|
* We also count the key we're currently using for KEX
|
|
|
|
* as one we've already got, because by the time this
|
|
|
|
* menu becomes visible, it will be.
|
Add manual cross-certification of new host keys.
If a server offers host key algorithms that we don't have a stored key
for, they will now appear in a submenu of the Special Commands menu.
Selecting one will force a repeat key exchange with that key, and if
it succeeds, will add the new host key to the cache. The idea is that
the new key sent by the server is protected by the crypto established
in the previous key exchange, so this is just as safe as typing some
command like 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub' at the
server prompt and transcribing the results manually.
This allows switching over to newer host key algorithms if the client
has begun to support them (e.g. people using PuTTY's new ECC
functionality for the first time), or if the server has acquired a new
key (e.g. due to a server OS upgrade).
At the moment, it's only available manually, for a single host key
type at a time. Automating it is potentially controversial for
security policy reasons (what if someone doesn't agree this is what
they want in their host key cache, or doesn't want to switch over to
using whichever of the keys PuTTY would now put top of the list?), for
code plumbing reasons (chaining several of these rekeys might be more
annoying than doing one at a time) and for CPU usage reasons (rekeys
are expensive), but even so, it might turn out to be a good idea in
future.
2016-03-21 07:25:31 +00:00
|
|
|
*/
|
|
|
|
ssh->n_uncert_hostkeys = 0;
|
|
|
|
|
|
|
|
for (j = 0; j < lenof(hostkey_algs); j++) {
|
2018-06-03 11:58:05 +00:00
|
|
|
if (hostkey_algs[j].alg != ssh->hostkey_alg &&
|
|
|
|
in_commasep_string(hostkey_algs[j].alg->ssh_id,
|
2018-05-27 17:13:53 +00:00
|
|
|
str.ptr, str.len) &&
|
Add manual cross-certification of new host keys.
If a server offers host key algorithms that we don't have a stored key
for, they will now appear in a submenu of the Special Commands menu.
Selecting one will force a repeat key exchange with that key, and if
it succeeds, will add the new host key to the cache. The idea is that
the new key sent by the server is protected by the crypto established
in the previous key exchange, so this is just as safe as typing some
command like 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub' at the
server prompt and transcribing the results manually.
This allows switching over to newer host key algorithms if the client
has begun to support them (e.g. people using PuTTY's new ECC
functionality for the first time), or if the server has acquired a new
key (e.g. due to a server OS upgrade).
At the moment, it's only available manually, for a single host key
type at a time. Automating it is potentially controversial for
security policy reasons (what if someone doesn't agree this is what
they want in their host key cache, or doesn't want to switch over to
using whichever of the keys PuTTY would now put top of the list?), for
code plumbing reasons (chaining several of these rekeys might be more
annoying than doing one at a time) and for CPU usage reasons (rekeys
are expensive), but even so, it might turn out to be a good idea in
future.
2016-03-21 07:25:31 +00:00
|
|
|
!have_ssh_host_key(ssh->savedhost, ssh->savedport,
|
2018-06-03 11:58:05 +00:00
|
|
|
hostkey_algs[j].alg->cache_id)) {
|
Add manual cross-certification of new host keys.
If a server offers host key algorithms that we don't have a stored key
for, they will now appear in a submenu of the Special Commands menu.
Selecting one will force a repeat key exchange with that key, and if
it succeeds, will add the new host key to the cache. The idea is that
the new key sent by the server is protected by the crypto established
in the previous key exchange, so this is just as safe as typing some
command like 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub' at the
server prompt and transcribing the results manually.
This allows switching over to newer host key algorithms if the client
has begun to support them (e.g. people using PuTTY's new ECC
functionality for the first time), or if the server has acquired a new
key (e.g. due to a server OS upgrade).
At the moment, it's only available manually, for a single host key
type at a time. Automating it is potentially controversial for
security policy reasons (what if someone doesn't agree this is what
they want in their host key cache, or doesn't want to switch over to
using whichever of the keys PuTTY would now put top of the list?), for
code plumbing reasons (chaining several of these rekeys might be more
annoying than doing one at a time) and for CPU usage reasons (rekeys
are expensive), but even so, it might turn out to be a good idea in
future.
2016-03-21 07:25:31 +00:00
|
|
|
ssh->uncert_hostkeys[ssh->n_uncert_hostkeys++] = j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
2015-06-07 12:40:11 +00:00
|
|
|
|
2011-03-04 22:34:47 +00:00
|
|
|
if (s->pending_compression) {
|
|
|
|
logevent("Server supports delayed compression; "
|
|
|
|
"will try this later");
|
|
|
|
}
|
2018-05-27 17:13:53 +00:00
|
|
|
get_string(pktin); /* client->server language */
|
|
|
|
get_string(pktin); /* server->client language */
|
|
|
|
s->ignorepkt = get_bool(pktin) && !s->guessok;
|
2005-02-17 18:56:37 +00:00
|
|
|
|
2018-09-13 15:41:46 +00:00
|
|
|
ssh->exhash = ssh_hash_new(ssh->kex->hash);
|
|
|
|
put_stringz(ssh->exhash, ssh->v_c);
|
|
|
|
put_stringz(ssh->exhash, ssh->v_s);
|
|
|
|
put_string(ssh->exhash, s->our_kexinit, s->our_kexinitlen);
|
2013-07-14 10:46:27 +00:00
|
|
|
sfree(s->our_kexinit);
|
2013-11-17 14:04:18 +00:00
|
|
|
/* Include the type byte in the hash of server's KEXINIT */
|
2018-09-13 15:41:46 +00:00
|
|
|
put_string(ssh->exhash,
|
2018-06-06 06:17:09 +00:00
|
|
|
(const char *)BinarySource_UPCAST(pktin)->data - 1,
|
|
|
|
BinarySource_UPCAST(pktin)->len + 1);
|
2013-07-14 10:46:27 +00:00
|
|
|
|
2005-02-17 18:56:37 +00:00
|
|
|
if (s->warn_kex) {
|
|
|
|
s->dlgret = askalg(ssh->frontend, "key-exchange algorithm",
|
|
|
|
ssh->kex->name,
|
|
|
|
ssh_dialog_callback, ssh);
|
|
|
|
if (s->dlgret < 0) {
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->user_response = -1;
|
|
|
|
crWaitUntilV(ssh->user_response >= 0);
|
2005-02-17 18:56:37 +00:00
|
|
|
s->dlgret = ssh->user_response;
|
|
|
|
}
|
|
|
|
if (s->dlgret == 0) {
|
2005-05-21 16:49:27 +00:00
|
|
|
ssh_disconnect(ssh, "User aborted at kex warning", NULL,
|
|
|
|
0, TRUE);
|
2012-08-27 22:02:17 +00:00
|
|
|
crStopV;
|
2005-02-17 18:56:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-25 15:56:31 +00:00
|
|
|
if (s->warn_hk) {
|
2016-03-27 17:08:49 +00:00
|
|
|
int j, k;
|
|
|
|
char *betteralgs;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Change warning box wording depending on why we chose a
|
|
|
|
* warning-level host key algorithm. If it's because
|
|
|
|
* that's all we have *cached*, use the askhk mechanism,
|
|
|
|
* and list the host keys we could usefully cross-certify.
|
|
|
|
* Otherwise, use askalg for the standard wording.
|
|
|
|
*/
|
|
|
|
betteralgs = NULL;
|
|
|
|
for (j = 0; j < ssh->n_uncert_hostkeys; j++) {
|
|
|
|
const struct ssh_signkey_with_user_pref_id *hktype =
|
|
|
|
&hostkey_algs[ssh->uncert_hostkeys[j]];
|
|
|
|
int better = FALSE;
|
|
|
|
for (k = 0; k < HK_MAX; k++) {
|
|
|
|
int id = conf_get_int_int(ssh->conf, CONF_ssh_hklist, k);
|
|
|
|
if (id == HK_WARN) {
|
|
|
|
break;
|
|
|
|
} else if (id == hktype->id) {
|
|
|
|
better = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (better) {
|
|
|
|
if (betteralgs) {
|
|
|
|
char *old_ba = betteralgs;
|
|
|
|
betteralgs = dupcat(betteralgs, ",",
|
2018-06-03 11:58:05 +00:00
|
|
|
hktype->alg->ssh_id,
|
2016-03-27 17:08:49 +00:00
|
|
|
(const char *)NULL);
|
|
|
|
sfree(old_ba);
|
|
|
|
} else {
|
2018-06-03 11:58:05 +00:00
|
|
|
betteralgs = dupstr(hktype->alg->ssh_id);
|
2016-03-27 17:08:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (betteralgs) {
|
2018-06-03 11:58:05 +00:00
|
|
|
s->dlgret = askhk(ssh->frontend, ssh->hostkey_alg->ssh_id,
|
2016-03-27 17:08:49 +00:00
|
|
|
betteralgs, ssh_dialog_callback, ssh);
|
|
|
|
sfree(betteralgs);
|
|
|
|
} else {
|
|
|
|
s->dlgret = askalg(ssh->frontend, "host key type",
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh->hostkey_alg->ssh_id,
|
2016-03-27 17:08:49 +00:00
|
|
|
ssh_dialog_callback, ssh);
|
|
|
|
}
|
2016-03-25 15:56:31 +00:00
|
|
|
if (s->dlgret < 0) {
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->user_response = -1;
|
|
|
|
crWaitUntilV(ssh->user_response >= 0);
|
2016-03-25 15:56:31 +00:00
|
|
|
s->dlgret = ssh->user_response;
|
|
|
|
}
|
|
|
|
if (s->dlgret == 0) {
|
|
|
|
ssh_disconnect(ssh, "User aborted at host key warning", NULL,
|
|
|
|
0, TRUE);
|
|
|
|
crStopV;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-02-17 18:56:37 +00:00
|
|
|
if (s->warn_cscipher) {
|
|
|
|
s->dlgret = askalg(ssh->frontend,
|
|
|
|
"client-to-server cipher",
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->out.cipher->name,
|
2005-02-17 18:56:37 +00:00
|
|
|
ssh_dialog_callback, ssh);
|
|
|
|
if (s->dlgret < 0) {
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->user_response = -1;
|
|
|
|
crWaitUntilV(ssh->user_response >= 0);
|
2005-02-17 18:56:37 +00:00
|
|
|
s->dlgret = ssh->user_response;
|
|
|
|
}
|
|
|
|
if (s->dlgret == 0) {
|
2005-05-21 16:49:27 +00:00
|
|
|
ssh_disconnect(ssh, "User aborted at cipher warning", NULL,
|
|
|
|
0, TRUE);
|
2012-08-27 22:02:17 +00:00
|
|
|
crStopV;
|
2005-02-17 18:56:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->warn_sccipher) {
|
|
|
|
s->dlgret = askalg(ssh->frontend,
|
|
|
|
"server-to-client cipher",
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->in.cipher->name,
|
2005-02-17 18:56:37 +00:00
|
|
|
ssh_dialog_callback, ssh);
|
|
|
|
if (s->dlgret < 0) {
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->user_response = -1;
|
|
|
|
crWaitUntilV(ssh->user_response >= 0);
|
2005-02-17 18:56:37 +00:00
|
|
|
s->dlgret = ssh->user_response;
|
|
|
|
}
|
|
|
|
if (s->dlgret == 0) {
|
2005-05-21 16:49:27 +00:00
|
|
|
ssh_disconnect(ssh, "User aborted at cipher warning", NULL,
|
|
|
|
0, TRUE);
|
2012-08-27 22:02:17 +00:00
|
|
|
crStopV;
|
2005-02-17 18:56:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->ignorepkt) /* first_kex_packet_follows */
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL);
|
2000-09-05 14:28:17 +00:00
|
|
|
}
|
|
|
|
|
2007-04-30 22:09:26 +00:00
|
|
|
if (ssh->kex->main_type == KEXTYPE_DH) {
|
2007-05-01 12:26:44 +00:00
|
|
|
/*
|
|
|
|
* Work out the number of bits of key we will need from the
|
|
|
|
* key exchange. We start with the maximum key length of
|
|
|
|
* either cipher...
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
int csbits, scbits;
|
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
csbits = s->out.cipher ? s->out.cipher->real_keybits : 0;
|
|
|
|
scbits = s->in.cipher ? s->in.cipher->real_keybits : 0;
|
2007-05-01 12:26:44 +00:00
|
|
|
s->nbits = (csbits > scbits ? csbits : scbits);
|
|
|
|
}
|
|
|
|
/* The keys only have hlen-bit entropy, since they're based on
|
|
|
|
* a hash. So cap the key size at hlen bits. */
|
|
|
|
if (s->nbits > ssh->kex->hash->hlen * 8)
|
|
|
|
s->nbits = ssh->kex->hash->hlen * 8;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we're doing Diffie-Hellman group exchange, start by
|
|
|
|
* requesting a group.
|
|
|
|
*/
|
2015-05-15 09:12:08 +00:00
|
|
|
if (dh_is_gex(ssh->kex)) {
|
2007-05-01 12:26:44 +00:00
|
|
|
logevent("Doing Diffie-Hellman group exchange");
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->pls.kctx = SSH2_PKTCTX_DHGEX;
|
2007-05-01 12:26:44 +00:00
|
|
|
/*
|
|
|
|
* Work out how big a DH group we will need to allow that
|
|
|
|
* much data.
|
|
|
|
*/
|
|
|
|
s->pbits = 512 << ((s->nbits - 1) / 64);
|
2015-04-25 09:46:53 +00:00
|
|
|
if (s->pbits < DH_MIN_SIZE)
|
|
|
|
s->pbits = DH_MIN_SIZE;
|
|
|
|
if (s->pbits > DH_MAX_SIZE)
|
|
|
|
s->pbits = DH_MAX_SIZE;
|
|
|
|
if ((ssh->remote_bugs & BUG_SSH2_OLDGEX)) {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH2_MSG_KEX_DH_GEX_REQUEST_OLD);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(s->pktout, s->pbits);
|
2015-04-25 09:46:53 +00:00
|
|
|
} else {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH2_MSG_KEX_DH_GEX_REQUEST);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(s->pktout, DH_MIN_SIZE);
|
|
|
|
put_uint32(s->pktout, s->pbits);
|
|
|
|
put_uint32(s->pktout, DH_MAX_SIZE);
|
2015-04-25 09:46:53 +00:00
|
|
|
}
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh_pkt_write(ssh, s->pktout);
|
2007-05-01 12:26:44 +00:00
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL);
|
2007-05-01 12:26:44 +00:00
|
|
|
if (pktin->type != SSH2_MSG_KEX_DH_GEX_GROUP) {
|
|
|
|
bombout(("expected key exchange group packet from server"));
|
2012-08-27 22:02:17 +00:00
|
|
|
crStopV;
|
2007-05-01 12:26:44 +00:00
|
|
|
}
|
2018-05-27 17:13:53 +00:00
|
|
|
s->p = get_mp_ssh2(pktin);
|
|
|
|
s->g = get_mp_ssh2(pktin);
|
|
|
|
if (get_err(pktin)) {
|
|
|
|
freebn(s->p);
|
|
|
|
freebn(s->g);
|
2007-05-01 12:26:44 +00:00
|
|
|
bombout(("unable to read mp-ints from incoming group packet"));
|
2012-08-27 22:02:17 +00:00
|
|
|
crStopV;
|
2007-05-01 12:26:44 +00:00
|
|
|
}
|
2018-09-14 07:48:54 +00:00
|
|
|
ssh->dh_ctx = dh_setup_gex(s->p, s->g);
|
2007-05-01 12:26:44 +00:00
|
|
|
s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
|
|
|
|
s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY;
|
|
|
|
} else {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->pls.kctx = SSH2_PKTCTX_DHGROUP;
|
2018-09-14 07:48:54 +00:00
|
|
|
ssh->dh_ctx = dh_setup_group(ssh->kex);
|
2007-05-01 12:26:44 +00:00
|
|
|
s->kex_init_value = SSH2_MSG_KEXDH_INIT;
|
|
|
|
s->kex_reply_value = SSH2_MSG_KEXDH_REPLY;
|
|
|
|
logeventf(ssh, "Using Diffie-Hellman with standard group \"%s\"",
|
|
|
|
ssh->kex->groupname);
|
|
|
|
}
|
2000-09-05 14:28:17 +00:00
|
|
|
|
2007-05-01 12:26:44 +00:00
|
|
|
logeventf(ssh, "Doing Diffie-Hellman key exchange with hash %s",
|
|
|
|
ssh->kex->hash->text_name);
|
|
|
|
/*
|
|
|
|
* Now generate and send e for Diffie-Hellman.
|
|
|
|
*/
|
|
|
|
set_busy_status(ssh->frontend, BUSY_CPU); /* this can take a while */
|
2018-09-14 07:48:54 +00:00
|
|
|
s->e = dh_create_e(ssh->dh_ctx, s->nbits * 2);
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(ssh->bpp, s->kex_init_value);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_mp_ssh2(s->pktout, s->e);
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh_pkt_write(ssh, s->pktout);
|
2007-05-01 12:26:44 +00:00
|
|
|
|
|
|
|
set_busy_status(ssh->frontend, BUSY_WAITING); /* wait for server */
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL);
|
2007-05-01 12:26:44 +00:00
|
|
|
if (pktin->type != s->kex_reply_value) {
|
|
|
|
bombout(("expected key exchange reply packet from server"));
|
2012-08-27 22:02:17 +00:00
|
|
|
crStopV;
|
2007-05-01 12:26:44 +00:00
|
|
|
}
|
|
|
|
set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
|
2018-05-27 17:13:53 +00:00
|
|
|
s->hostkeydata = get_string(pktin);
|
2018-06-03 11:58:05 +00:00
|
|
|
s->hkey = ssh_key_new_pub(ssh->hostkey_alg, s->hostkeydata);
|
2018-05-27 17:13:53 +00:00
|
|
|
s->f = get_mp_ssh2(pktin);
|
|
|
|
s->sigdata = get_string(pktin);
|
|
|
|
if (get_err(pktin)) {
|
2015-06-13 14:22:03 +00:00
|
|
|
bombout(("unable to parse key exchange reply packet"));
|
|
|
|
crStopV;
|
|
|
|
}
|
2000-09-05 14:28:17 +00:00
|
|
|
|
2015-02-05 19:39:17 +00:00
|
|
|
{
|
2018-09-14 07:48:54 +00:00
|
|
|
const char *err = dh_validate_f(ssh->dh_ctx, s->f);
|
2015-02-05 19:39:17 +00:00
|
|
|
if (err) {
|
|
|
|
bombout(("key exchange reply failed validation: %s", err));
|
|
|
|
crStopV;
|
|
|
|
}
|
|
|
|
}
|
2018-09-14 07:48:54 +00:00
|
|
|
s->K = dh_find_K(ssh->dh_ctx, s->f);
|
2000-09-05 14:28:17 +00:00
|
|
|
|
2007-05-01 12:26:44 +00:00
|
|
|
/* We assume everything from now on will be quick, and it might
|
|
|
|
* involve user interaction. */
|
|
|
|
set_busy_status(ssh->frontend, BUSY_NOT);
|
2005-02-15 17:05:58 +00:00
|
|
|
|
2018-09-13 15:41:46 +00:00
|
|
|
put_stringpl(ssh->exhash, s->hostkeydata);
|
2015-05-15 09:12:08 +00:00
|
|
|
if (dh_is_gex(ssh->kex)) {
|
2015-04-25 09:46:53 +00:00
|
|
|
if (!(ssh->remote_bugs & BUG_SSH2_OLDGEX))
|
2018-09-13 15:41:46 +00:00
|
|
|
put_uint32(ssh->exhash, DH_MIN_SIZE);
|
|
|
|
put_uint32(ssh->exhash, s->pbits);
|
2015-04-25 09:46:53 +00:00
|
|
|
if (!(ssh->remote_bugs & BUG_SSH2_OLDGEX))
|
2018-09-13 15:41:46 +00:00
|
|
|
put_uint32(ssh->exhash, DH_MAX_SIZE);
|
|
|
|
put_mp_ssh2(ssh->exhash, s->p);
|
|
|
|
put_mp_ssh2(ssh->exhash, s->g);
|
2007-05-01 12:26:44 +00:00
|
|
|
}
|
2018-09-13 15:41:46 +00:00
|
|
|
put_mp_ssh2(ssh->exhash, s->e);
|
|
|
|
put_mp_ssh2(ssh->exhash, s->f);
|
2007-05-01 12:26:44 +00:00
|
|
|
|
2018-09-14 07:48:54 +00:00
|
|
|
dh_cleanup(ssh->dh_ctx);
|
2007-05-01 12:26:44 +00:00
|
|
|
freebn(s->f);
|
2015-05-15 09:12:08 +00:00
|
|
|
if (dh_is_gex(ssh->kex)) {
|
2007-05-01 12:26:44 +00:00
|
|
|
freebn(s->g);
|
|
|
|
freebn(s->p);
|
|
|
|
}
|
2014-11-01 09:45:20 +00:00
|
|
|
} else if (ssh->kex->main_type == KEXTYPE_ECDH) {
|
|
|
|
|
2015-05-19 07:42:23 +00:00
|
|
|
logeventf(ssh, "Doing ECDH key exchange with curve %s and hash %s",
|
|
|
|
ssh_ecdhkex_curve_textname(ssh->kex),
|
2014-11-01 09:45:20 +00:00
|
|
|
ssh->kex->hash->text_name);
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->pls.kctx = SSH2_PKTCTX_ECDHKEX;
|
2014-11-01 09:45:20 +00:00
|
|
|
|
2015-05-15 09:13:05 +00:00
|
|
|
s->eckey = ssh_ecdhkex_newkey(ssh->kex);
|
2014-11-01 09:45:20 +00:00
|
|
|
if (!s->eckey) {
|
|
|
|
bombout(("Unable to generate key for ECDH"));
|
|
|
|
crStopV;
|
|
|
|
}
|
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_KEX_ECDH_INIT);
|
2014-11-01 09:45:20 +00:00
|
|
|
{
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf *pubpoint = strbuf_new();
|
|
|
|
ssh_ecdhkex_getpublic(s->eckey, BinarySink_UPCAST(pubpoint));
|
|
|
|
put_stringsb(s->pktout, pubpoint);
|
2014-11-01 09:45:20 +00:00
|
|
|
}
|
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh_pkt_write(ssh, s->pktout);
|
2014-11-01 09:45:20 +00:00
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL);
|
2014-11-01 09:45:20 +00:00
|
|
|
if (pktin->type != SSH2_MSG_KEX_ECDH_REPLY) {
|
|
|
|
ssh_ecdhkex_freekey(s->eckey);
|
|
|
|
bombout(("expected ECDH reply packet from server"));
|
|
|
|
crStopV;
|
|
|
|
}
|
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
s->hostkeydata = get_string(pktin);
|
2018-09-13 15:41:46 +00:00
|
|
|
put_stringpl(ssh->exhash, s->hostkeydata);
|
2018-06-03 11:58:05 +00:00
|
|
|
s->hkey = ssh_key_new_pub(ssh->hostkey_alg, s->hostkeydata);
|
2014-11-01 09:45:20 +00:00
|
|
|
|
|
|
|
{
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf *pubpoint = strbuf_new();
|
|
|
|
ssh_ecdhkex_getpublic(s->eckey, BinarySink_UPCAST(pubpoint));
|
2018-09-13 15:41:46 +00:00
|
|
|
put_string(ssh->exhash, pubpoint->u, pubpoint->len);
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf_free(pubpoint);
|
2014-11-01 09:45:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen keydata = get_string(pktin);
|
2018-09-13 15:41:46 +00:00
|
|
|
put_stringpl(ssh->exhash, keydata);
|
2018-05-27 17:13:53 +00:00
|
|
|
s->K = ssh_ecdhkex_getkey(s->eckey, keydata.ptr, keydata.len);
|
|
|
|
if (!get_err(pktin) && !s->K) {
|
2014-11-01 09:45:20 +00:00
|
|
|
ssh_ecdhkex_freekey(s->eckey);
|
|
|
|
bombout(("point received in ECDH was not valid"));
|
|
|
|
crStopV;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
s->sigdata = get_string(pktin);
|
|
|
|
if (get_err(pktin)) {
|
2015-06-13 14:22:03 +00:00
|
|
|
bombout(("unable to parse key exchange reply packet"));
|
|
|
|
crStopV;
|
|
|
|
}
|
2014-11-01 09:45:20 +00:00
|
|
|
|
|
|
|
ssh_ecdhkex_freekey(s->eckey);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
#ifndef NO_GSSAPI
|
|
|
|
} else if (ssh->kex->main_type == KEXTYPE_GSS) {
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen data;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->pls.kctx = SSH2_PKTCTX_GSSKEX;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
s->init_token_sent = 0;
|
|
|
|
s->complete_rcvd = 0;
|
|
|
|
s->hkey = NULL;
|
|
|
|
s->fingerprint = NULL;
|
|
|
|
s->keystr = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Work out the number of bits of key we will need from the
|
|
|
|
* key exchange. We start with the maximum key length of
|
|
|
|
* either cipher...
|
|
|
|
*
|
|
|
|
* This is rote from the KEXTYPE_DH section above.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
int csbits, scbits;
|
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
csbits = s->out.cipher->real_keybits;
|
|
|
|
scbits = s->in.cipher->real_keybits;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
s->nbits = (csbits > scbits ? csbits : scbits);
|
|
|
|
}
|
|
|
|
/* The keys only have hlen-bit entropy, since they're based on
|
|
|
|
* a hash. So cap the key size at hlen bits. */
|
|
|
|
if (s->nbits > ssh->kex->hash->hlen * 8)
|
|
|
|
s->nbits = ssh->kex->hash->hlen * 8;
|
|
|
|
|
|
|
|
if (dh_is_gex(ssh->kex)) {
|
|
|
|
/*
|
|
|
|
* Work out how big a DH group we will need to allow that
|
|
|
|
* much data.
|
|
|
|
*/
|
|
|
|
s->pbits = 512 << ((s->nbits - 1) / 64);
|
|
|
|
logeventf(ssh, "Doing GSSAPI (with Kerberos V5) Diffie-Hellman "
|
|
|
|
"group exchange, with minimum %d bits", s->pbits);
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_KEXGSS_GROUPREQ);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(s->pktout, s->pbits); /* min */
|
|
|
|
put_uint32(s->pktout, s->pbits); /* preferred */
|
|
|
|
put_uint32(s->pktout, s->pbits * 2); /* max */
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh_pkt_write(ssh, s->pktout);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV(
|
|
|
|
(pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
if (pktin->type != SSH2_MSG_KEXGSS_GROUP) {
|
|
|
|
bombout(("expected key exchange group packet from server"));
|
|
|
|
crStopV;
|
|
|
|
}
|
2018-05-27 17:13:53 +00:00
|
|
|
s->p = get_mp_ssh2(pktin);
|
|
|
|
s->g = get_mp_ssh2(pktin);
|
|
|
|
if (get_err(pktin)) {
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
bombout(("unable to read mp-ints from incoming group packet"));
|
|
|
|
crStopV;
|
|
|
|
}
|
2018-09-14 07:48:54 +00:00
|
|
|
ssh->dh_ctx = dh_setup_gex(s->p, s->g);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
} else {
|
2018-09-14 07:48:54 +00:00
|
|
|
ssh->dh_ctx = dh_setup_group(ssh->kex);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
logeventf(ssh, "Using GSSAPI (with Kerberos V5) Diffie-Hellman with standard group \"%s\"",
|
|
|
|
ssh->kex->groupname);
|
|
|
|
}
|
|
|
|
|
|
|
|
logeventf(ssh, "Doing GSSAPI (with Kerberos V5) Diffie-Hellman key exchange with hash %s",
|
|
|
|
ssh->kex->hash->text_name);
|
|
|
|
/* Now generate e for Diffie-Hellman. */
|
|
|
|
set_busy_status(ssh->frontend, BUSY_CPU); /* this can take a while */
|
2018-09-14 07:48:54 +00:00
|
|
|
s->e = dh_create_e(ssh->dh_ctx, s->nbits * 2);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
|
|
|
|
if (ssh->gsslib->gsslogmsg)
|
|
|
|
logevent(ssh->gsslib->gsslogmsg);
|
|
|
|
|
|
|
|
/* initial tokens are empty */
|
|
|
|
SSH_GSS_CLEAR_BUF(&s->gss_rcvtok);
|
|
|
|
SSH_GSS_CLEAR_BUF(&s->gss_sndtok);
|
|
|
|
SSH_GSS_CLEAR_BUF(&s->mic);
|
|
|
|
s->gss_stat = ssh->gsslib->acquire_cred(ssh->gsslib, &s->gss_ctx,
|
|
|
|
&s->gss_cred_expiry);
|
|
|
|
if (s->gss_stat != SSH_GSS_OK) {
|
|
|
|
bombout(("GSSAPI key exchange failed to initialize"));
|
|
|
|
crStopV;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now enter the loop */
|
|
|
|
assert(ssh->gss_srv_name);
|
|
|
|
do {
|
|
|
|
/*
|
|
|
|
* When acquire_cred yields no useful expiration, go with the
|
|
|
|
* service ticket expiration.
|
|
|
|
*/
|
|
|
|
s->gss_stat = ssh->gsslib->init_sec_context
|
|
|
|
(ssh->gsslib,
|
|
|
|
&s->gss_ctx,
|
|
|
|
ssh->gss_srv_name,
|
|
|
|
s->gss_delegate,
|
|
|
|
&s->gss_rcvtok,
|
|
|
|
&s->gss_sndtok,
|
|
|
|
(s->gss_cred_expiry == GSS_NO_EXPIRATION ?
|
|
|
|
&s->gss_cred_expiry : NULL),
|
|
|
|
NULL);
|
|
|
|
SSH_GSS_CLEAR_BUF(&s->gss_rcvtok);
|
|
|
|
|
|
|
|
if (s->gss_stat == SSH_GSS_S_COMPLETE && s->complete_rcvd)
|
|
|
|
break; /* MIC is verified after the loop */
|
|
|
|
|
|
|
|
if (s->gss_stat != SSH_GSS_S_COMPLETE &&
|
|
|
|
s->gss_stat != SSH_GSS_S_CONTINUE_NEEDED) {
|
|
|
|
if (ssh->gsslib->display_status(ssh->gsslib, s->gss_ctx,
|
|
|
|
&s->gss_buf) == SSH_GSS_OK) {
|
|
|
|
bombout(("GSSAPI key exchange failed to initialize"
|
|
|
|
" context: %s", (char *)s->gss_buf.value));
|
|
|
|
sfree(s->gss_buf.value);
|
|
|
|
crStopV;
|
|
|
|
} else {
|
|
|
|
bombout(("GSSAPI key exchange failed to initialize"
|
|
|
|
" context"));
|
|
|
|
crStopV;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(s->gss_stat == SSH_GSS_S_COMPLETE ||
|
|
|
|
s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED);
|
|
|
|
|
|
|
|
if (!s->init_token_sent) {
|
|
|
|
s->init_token_sent = 1;
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_KEXGSS_INIT);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
if (s->gss_sndtok.length == 0) {
|
|
|
|
bombout(("GSSAPI key exchange failed:"
|
|
|
|
" no initial context token"));
|
|
|
|
crStopV;
|
|
|
|
}
|
2018-05-24 09:15:36 +00:00
|
|
|
put_string(s->pktout,
|
|
|
|
s->gss_sndtok.value, s->gss_sndtok.length);
|
|
|
|
put_mp_ssh2(s->pktout, s->e);
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh_pkt_write(ssh, s->pktout);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
ssh->gsslib->free_tok(ssh->gsslib, &s->gss_sndtok);
|
|
|
|
logevent("GSSAPI key exchange initialised");
|
|
|
|
} else if (s->gss_sndtok.length != 0) {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH2_MSG_KEXGSS_CONTINUE);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_string(s->pktout,
|
|
|
|
s->gss_sndtok.value, s->gss_sndtok.length);
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh_pkt_write(ssh, s->pktout);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
ssh->gsslib->free_tok(ssh->gsslib, &s->gss_sndtok);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->gss_stat == SSH_GSS_S_COMPLETE && s->complete_rcvd)
|
|
|
|
break;
|
|
|
|
|
|
|
|
wait_for_gss_token:
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV(
|
|
|
|
(pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
switch (pktin->type) {
|
|
|
|
case SSH2_MSG_KEXGSS_CONTINUE:
|
2018-05-27 17:13:53 +00:00
|
|
|
data = get_string(pktin);
|
|
|
|
s->gss_rcvtok.value = (char *)data.ptr;
|
|
|
|
s->gss_rcvtok.length = data.len;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
continue;
|
|
|
|
case SSH2_MSG_KEXGSS_COMPLETE:
|
|
|
|
s->complete_rcvd = 1;
|
2018-05-27 17:13:53 +00:00
|
|
|
s->f = get_mp_ssh2(pktin);
|
|
|
|
data = get_string(pktin);
|
|
|
|
s->mic.value = (char *)data.ptr;
|
|
|
|
s->mic.length = data.len;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
/* Save expiration time of cred when delegating */
|
|
|
|
if (s->gss_delegate && s->gss_cred_expiry != GSS_NO_EXPIRATION)
|
|
|
|
ssh->gss_cred_expiry = s->gss_cred_expiry;
|
|
|
|
/* If there's a final token we loop to consume it */
|
2018-05-27 17:13:53 +00:00
|
|
|
if (get_bool(pktin)) {
|
|
|
|
data = get_string(pktin);
|
|
|
|
s->gss_rcvtok.value = (char *)data.ptr;
|
|
|
|
s->gss_rcvtok.length = data.len;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SSH2_MSG_KEXGSS_HOSTKEY:
|
2018-05-27 17:13:53 +00:00
|
|
|
s->hostkeydata = get_string(pktin);
|
2018-06-03 11:58:05 +00:00
|
|
|
if (ssh->hostkey_alg) {
|
|
|
|
s->hkey = ssh_key_new_pub(ssh->hostkey_alg,
|
|
|
|
s->hostkeydata);
|
2018-09-13 15:41:46 +00:00
|
|
|
put_string(ssh->exhash,
|
2018-05-27 17:13:53 +00:00
|
|
|
s->hostkeydata.ptr, s->hostkeydata.len);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Can't loop as we have no token to pass to
|
|
|
|
* init_sec_context.
|
|
|
|
*/
|
|
|
|
goto wait_for_gss_token;
|
|
|
|
case SSH2_MSG_KEXGSS_ERROR:
|
|
|
|
/*
|
|
|
|
* We have no use for the server's major and minor
|
|
|
|
* status. The minor status is really only
|
|
|
|
* meaningful to the server, and with luck the major
|
|
|
|
* status means something to us (but not really all
|
|
|
|
* that much). The string is more meaningful, and
|
|
|
|
* hopefully the server sends any error tokens, as
|
|
|
|
* that will produce the most useful information for
|
|
|
|
* us.
|
|
|
|
*/
|
2018-05-27 17:13:53 +00:00
|
|
|
get_uint32(pktin); /* server's major status */
|
|
|
|
get_uint32(pktin); /* server's minor status */
|
|
|
|
data = get_string(pktin);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
logeventf(ssh, "GSSAPI key exchange failed; "
|
2018-05-27 17:13:53 +00:00
|
|
|
"server's message: %.*s", PTRLEN_PRINTF(data));
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
/* Language tag, but we have no use for it */
|
2018-05-27 17:13:53 +00:00
|
|
|
get_string(pktin);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
/*
|
|
|
|
* Wait for an error token, if there is one, or the
|
|
|
|
* server's disconnect. The error token, if there
|
|
|
|
* is one, must follow the SSH2_MSG_KEXGSS_ERROR
|
|
|
|
* message, per the RFC.
|
|
|
|
*/
|
|
|
|
goto wait_for_gss_token;
|
|
|
|
default:
|
|
|
|
bombout(("unexpected message type during gss kex"));
|
|
|
|
crStopV;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (s->gss_rcvtok.length ||
|
|
|
|
s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED ||
|
|
|
|
!s->complete_rcvd);
|
|
|
|
|
2018-09-14 07:48:54 +00:00
|
|
|
s->K = dh_find_K(ssh->dh_ctx, s->f);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
|
|
|
|
/* We assume everything from now on will be quick, and it might
|
|
|
|
* involve user interaction. */
|
|
|
|
set_busy_status(ssh->frontend, BUSY_NOT);
|
|
|
|
|
|
|
|
if (!s->hkey)
|
2018-09-13 15:41:46 +00:00
|
|
|
put_stringz(ssh->exhash, "");
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
if (dh_is_gex(ssh->kex)) {
|
|
|
|
/* min, preferred, max */
|
2018-09-13 15:41:46 +00:00
|
|
|
put_uint32(ssh->exhash, s->pbits);
|
|
|
|
put_uint32(ssh->exhash, s->pbits);
|
|
|
|
put_uint32(ssh->exhash, s->pbits * 2);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
|
2018-09-13 15:41:46 +00:00
|
|
|
put_mp_ssh2(ssh->exhash, s->p);
|
|
|
|
put_mp_ssh2(ssh->exhash, s->g);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
}
|
2018-09-13 15:41:46 +00:00
|
|
|
put_mp_ssh2(ssh->exhash, s->e);
|
|
|
|
put_mp_ssh2(ssh->exhash, s->f);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* MIC verification is done below, after we compute the hash
|
|
|
|
* used as the MIC input.
|
|
|
|
*/
|
|
|
|
|
2018-09-14 07:48:54 +00:00
|
|
|
dh_cleanup(ssh->dh_ctx);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
freebn(s->f);
|
|
|
|
if (dh_is_gex(ssh->kex)) {
|
|
|
|
freebn(s->g);
|
|
|
|
freebn(s->p);
|
|
|
|
}
|
|
|
|
#endif
|
2007-04-30 22:09:26 +00:00
|
|
|
} else {
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen rsakeydata;
|
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
assert(ssh->kex->main_type == KEXTYPE_RSA);
|
2007-04-30 22:09:26 +00:00
|
|
|
logeventf(ssh, "Doing RSA key exchange with hash %s",
|
|
|
|
ssh->kex->hash->text_name);
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->pls.kctx = SSH2_PKTCTX_RSAKEX;
|
2007-04-30 22:09:26 +00:00
|
|
|
/*
|
|
|
|
* RSA key exchange. First expect a KEXRSA_PUBKEY packet
|
|
|
|
* from the server.
|
|
|
|
*/
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL);
|
2007-04-30 22:09:26 +00:00
|
|
|
if (pktin->type != SSH2_MSG_KEXRSA_PUBKEY) {
|
|
|
|
bombout(("expected RSA public key packet from server"));
|
2012-08-27 22:02:17 +00:00
|
|
|
crStopV;
|
2007-04-30 22:09:26 +00:00
|
|
|
}
|
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
s->hostkeydata = get_string(pktin);
|
2018-09-13 15:41:46 +00:00
|
|
|
put_stringpl(ssh->exhash, s->hostkeydata);
|
2018-06-03 11:58:05 +00:00
|
|
|
s->hkey = ssh_key_new_pub(ssh->hostkey_alg, s->hostkeydata);
|
2007-04-30 22:09:26 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
rsakeydata = get_string(pktin);
|
2007-04-30 22:09:26 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
s->rsakey = ssh_rsakex_newkey(rsakeydata.ptr, rsakeydata.len);
|
2007-04-30 22:09:26 +00:00
|
|
|
if (!s->rsakey) {
|
|
|
|
bombout(("unable to parse RSA public key from server"));
|
2012-08-27 22:02:17 +00:00
|
|
|
crStopV;
|
2007-04-30 22:09:26 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 15:41:46 +00:00
|
|
|
put_stringpl(ssh->exhash, rsakeydata);
|
2007-04-30 22:09:26 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Next, set up a shared secret K, of precisely KLEN -
|
|
|
|
* 2*HLEN - 49 bits, where KLEN is the bit length of the
|
|
|
|
* RSA key modulus and HLEN is the bit length of the hash
|
|
|
|
* we're using.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
int klen = ssh_rsakex_klen(s->rsakey);
|
|
|
|
int nbits = klen - (2*ssh->kex->hash->hlen*8 + 49);
|
|
|
|
int i, byte = 0;
|
2018-05-24 09:15:36 +00:00
|
|
|
strbuf *buf;
|
|
|
|
unsigned char *outstr;
|
|
|
|
int outstrlen;
|
2007-04-30 22:09:26 +00:00
|
|
|
|
|
|
|
s->K = bn_power_2(nbits - 1);
|
|
|
|
|
|
|
|
for (i = 0; i < nbits; i++) {
|
|
|
|
if ((i & 7) == 0) {
|
|
|
|
byte = random_byte();
|
|
|
|
}
|
|
|
|
bignum_set_bit(s->K, i, (byte >> (i & 7)) & 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encode this as an mpint.
|
|
|
|
*/
|
2018-05-24 09:15:36 +00:00
|
|
|
buf = strbuf_new();
|
|
|
|
put_mp_ssh2(buf, s->K);
|
2007-04-30 22:09:26 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Encrypt it with the given RSA key.
|
|
|
|
*/
|
|
|
|
outstrlen = (klen + 7) / 8;
|
|
|
|
outstr = snewn(outstrlen, unsigned char);
|
2018-05-24 09:15:36 +00:00
|
|
|
ssh_rsakex_encrypt(ssh->kex->hash, buf->u, buf->len,
|
2007-04-30 22:09:26 +00:00
|
|
|
outstr, outstrlen, s->rsakey);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* And send it off in a return packet.
|
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_KEXRSA_SECRET);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_string(s->pktout, outstr, outstrlen);
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh_pkt_write(ssh, s->pktout);
|
2007-04-30 22:09:26 +00:00
|
|
|
|
2018-09-13 15:41:46 +00:00
|
|
|
put_string(ssh->exhash, outstr, outstrlen);
|
2007-04-30 22:09:26 +00:00
|
|
|
|
2018-05-24 09:15:36 +00:00
|
|
|
strbuf_free(buf);
|
2007-04-30 22:09:26 +00:00
|
|
|
sfree(outstr);
|
|
|
|
}
|
|
|
|
|
|
|
|
ssh_rsakex_freekey(s->rsakey);
|
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL);
|
2007-04-30 22:09:26 +00:00
|
|
|
if (pktin->type != SSH2_MSG_KEXRSA_DONE) {
|
|
|
|
bombout(("expected signature packet from server"));
|
2012-08-27 22:02:17 +00:00
|
|
|
crStopV;
|
2007-04-30 22:09:26 +00:00
|
|
|
}
|
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
s->sigdata = get_string(pktin);
|
|
|
|
if (get_err(pktin)) {
|
2015-06-13 14:22:03 +00:00
|
|
|
bombout(("unable to parse signature packet"));
|
|
|
|
crStopV;
|
|
|
|
}
|
2007-04-30 22:09:26 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 15:41:46 +00:00
|
|
|
put_mp_ssh2(ssh->exhash, s->K);
|
|
|
|
assert(ssh_hash_alg(ssh->exhash)->hlen <= sizeof(s->exchange_hash));
|
|
|
|
ssh_hash_final(ssh->exhash, s->exchange_hash);
|
2000-09-05 14:28:17 +00:00
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
#ifndef NO_GSSAPI
|
|
|
|
if (ssh->kex->main_type == KEXTYPE_GSS) {
|
|
|
|
Ssh_gss_buf gss_buf;
|
|
|
|
SSH_GSS_CLEAR_BUF(&s->gss_buf);
|
|
|
|
|
|
|
|
gss_buf.value = s->exchange_hash;
|
|
|
|
gss_buf.length = ssh->kex->hash->hlen;
|
|
|
|
s->gss_stat = ssh->gsslib->verify_mic(ssh->gsslib, s->gss_ctx, &gss_buf, &s->mic);
|
|
|
|
if (s->gss_stat != SSH_GSS_OK) {
|
|
|
|
if (ssh->gsslib->display_status(ssh->gsslib, s->gss_ctx,
|
|
|
|
&s->gss_buf) == SSH_GSS_OK) {
|
|
|
|
bombout(("GSSAPI Key Exchange MIC was not valid: %s",
|
|
|
|
(char *)s->gss_buf.value));
|
|
|
|
sfree(s->gss_buf.value);
|
|
|
|
} else {
|
|
|
|
bombout(("GSSAPI Key Exchange MIC was not valid"));
|
|
|
|
}
|
|
|
|
crStopV;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssh->gss_kex_used = TRUE;
|
|
|
|
|
|
|
|
/*-
|
|
|
|
* If this the first KEX, save the GSS context for "gssapi-keyex"
|
|
|
|
* authentication.
|
|
|
|
*
|
|
|
|
* http://tools.ietf.org/html/rfc4462#section-4
|
|
|
|
*
|
|
|
|
* This method may be used only if the initial key exchange was
|
|
|
|
* performed using a GSS-API-based key exchange method defined in
|
|
|
|
* accordance with Section 2. The GSS-API context used with this
|
|
|
|
* method is always that established during an initial GSS-API-based
|
|
|
|
* key exchange. Any context established during key exchange for the
|
|
|
|
* purpose of rekeying MUST NOT be used with this method.
|
|
|
|
*/
|
|
|
|
if (!s->got_session_id) {
|
|
|
|
ssh->gss_ctx = s->gss_ctx;
|
|
|
|
} else {
|
|
|
|
ssh->gsslib->release_cred(ssh->gsslib, &s->gss_ctx);
|
|
|
|
}
|
|
|
|
logeventf(ssh, "GSSAPI Key Exchange complete!");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-09-14 07:48:54 +00:00
|
|
|
ssh->dh_ctx = NULL;
|
2001-03-01 17:41:26 +00:00
|
|
|
|
2000-09-07 16:33:49 +00:00
|
|
|
#if 0
|
2001-04-28 17:35:18 +00:00
|
|
|
debug(("Exchange hash is:\n"));
|
2005-08-31 20:43:06 +00:00
|
|
|
dmemdump(s->exchange_hash, ssh->kex->hash->hlen);
|
2000-09-07 16:33:49 +00:00
|
|
|
#endif
|
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
/* In GSS keyex there's no hostkey signature to verify */
|
|
|
|
if (ssh->kex->main_type != KEXTYPE_GSS) {
|
|
|
|
if (!s->hkey) {
|
|
|
|
bombout(("Server's host key is invalid"));
|
|
|
|
crStopV;
|
|
|
|
}
|
2015-10-18 19:16:39 +00:00
|
|
|
|
2018-06-03 11:58:05 +00:00
|
|
|
if (!ssh_key_verify(
|
Clean up ssh_keyalg APIs and implementations.
Quite a few of the function pointers in the ssh_keyalg vtable now take
ptrlen arguments in place of separate pointer and length pairs.
Meanwhile, the various key types' implementations of those functions
now work by initialising a BinarySource with the input ptrlen and
using the new decode functions to walk along it.
One exception is the openssh_createkey method which reads a private
key in the wire format used by OpenSSH's SSH-2 agent protocol, which
has to consume a prefix of a larger data stream, and tell the caller
how much of that data was the private key. That function now takes an
actual BinarySource, and passes that directly to the decode functions,
so that on return the caller finds that the BinarySource's read
pointer has been advanced exactly past the private key.
This let me throw away _several_ reimplementations of mpint-reading
functions, one in each of sshrsa, sshdss.c and sshecc.c. Worse still,
they didn't all have exactly the SSH-2 semantics, because the thing in
sshrsa.c whose name suggested it was an mpint-reading function
actually tolerated the wrong number of leading zero bytes, which it
had to be able to do to cope with the "ssh-rsa" signature format which
contains a thing that isn't quite an SSH-2 mpint. Now that deviation
is clearly commented!
2018-05-31 17:40:51 +00:00
|
|
|
s->hkey, s->sigdata,
|
|
|
|
make_ptrlen(s->exchange_hash, ssh->kex->hash->hlen))) {
|
2015-10-18 12:04:58 +00:00
|
|
|
#ifndef FUZZING
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
bombout(("Server's host key did not match the signature "
|
|
|
|
"supplied"));
|
|
|
|
crStopV;
|
2015-10-18 12:04:58 +00:00
|
|
|
#endif
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
}
|
2000-09-22 11:04:57 +00:00
|
|
|
}
|
2000-09-05 14:28:17 +00:00
|
|
|
|
2018-06-03 11:58:05 +00:00
|
|
|
s->keystr = (s->hkey ? ssh_key_cache_str(s->hkey) : NULL);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
#ifndef NO_GSSAPI
|
|
|
|
if (ssh->gss_kex_used) {
|
|
|
|
/*
|
|
|
|
* In a GSS-based session, check the host key (if any) against
|
|
|
|
* the transient host key cache. See comment above, at the
|
|
|
|
* definition of ssh_transient_hostkey_cache_entry.
|
|
|
|
*/
|
|
|
|
if (ssh->kex->main_type == KEXTYPE_GSS) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We've just done a GSS key exchange. If it gave us a
|
|
|
|
* host key, store it.
|
|
|
|
*/
|
|
|
|
if (s->hkey) {
|
2018-06-03 11:58:05 +00:00
|
|
|
s->fingerprint = ssh2_fingerprint(s->hkey);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
logevent("GSS kex provided fallback host key:");
|
|
|
|
logevent(s->fingerprint);
|
|
|
|
sfree(s->fingerprint);
|
|
|
|
s->fingerprint = NULL;
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh_store_transient_hostkey(ssh, s->hkey);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
} else if (!ssh_have_any_transient_hostkey(ssh)) {
|
|
|
|
/*
|
|
|
|
* But if it didn't, then we currently have no
|
|
|
|
* fallback host key to use in subsequent non-GSS
|
|
|
|
* rekeys. So we should immediately trigger a non-GSS
|
|
|
|
* rekey of our own, to set one up, before the session
|
|
|
|
* keys have been used for anything else.
|
|
|
|
*
|
|
|
|
* This is similar to the cross-certification done at
|
|
|
|
* user request in the permanent host key cache, but
|
|
|
|
* here we do it automatically, once, at session
|
|
|
|
* startup, and only add the key to the transient
|
|
|
|
* cache.
|
|
|
|
*/
|
2018-06-03 11:58:05 +00:00
|
|
|
if (ssh->hostkey_alg) {
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
s->need_gss_transient_hostkey = TRUE;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* If we negotiated the "null" host key algorithm
|
|
|
|
* in the key exchange, that's an indication that
|
|
|
|
* no host key at all is available from the server
|
|
|
|
* (both because we listed "null" last, and
|
|
|
|
* because RFC 4462 section 5 says that a server
|
|
|
|
* MUST NOT offer "null" as a host key algorithm
|
|
|
|
* unless that is the only algorithm it provides
|
|
|
|
* at all).
|
|
|
|
*
|
|
|
|
* In that case we actually _can't_ perform a
|
|
|
|
* non-GSSAPI key exchange, so it's pointless to
|
|
|
|
* attempt one proactively. This is also likely to
|
|
|
|
* cause trouble later if a rekey is required at a
|
|
|
|
* moment whne GSS credentials are not available,
|
|
|
|
* but someone setting up a server in this
|
|
|
|
* configuration presumably accepts that as a
|
|
|
|
* consequence.
|
|
|
|
*/
|
|
|
|
if (!s->warned_about_no_gss_transient_hostkey) {
|
|
|
|
logevent("No fallback host key available");
|
|
|
|
s->warned_about_no_gss_transient_hostkey = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We've just done a fallback key exchange, so make
|
|
|
|
* sure the host key it used is in the cache of keys
|
|
|
|
* we previously received in GSS kexes.
|
|
|
|
*
|
|
|
|
* An exception is if this was the non-GSS key exchange we
|
|
|
|
* triggered on purpose to populate the transient cache.
|
|
|
|
*/
|
|
|
|
assert(s->hkey); /* only KEXTYPE_GSS lets this be null */
|
2018-06-03 11:58:05 +00:00
|
|
|
s->fingerprint = ssh2_fingerprint(s->hkey);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
|
|
|
|
if (s->need_gss_transient_hostkey) {
|
|
|
|
logevent("Post-GSS rekey provided fallback host key:");
|
|
|
|
logevent(s->fingerprint);
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh_store_transient_hostkey(ssh, s->hkey);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
s->need_gss_transient_hostkey = FALSE;
|
2018-06-03 11:58:05 +00:00
|
|
|
} else if (!ssh_verify_transient_hostkey(ssh, s->hkey)) {
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
logevent("Non-GSS rekey after initial GSS kex "
|
|
|
|
"used host key:");
|
|
|
|
logevent(s->fingerprint);
|
|
|
|
bombout(("Host key was not previously sent via GSS kex"));
|
|
|
|
}
|
|
|
|
|
|
|
|
sfree(s->fingerprint);
|
|
|
|
s->fingerprint = NULL;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
#endif /* NO_GSSAPI */
|
2013-08-18 06:48:20 +00:00
|
|
|
if (!s->got_session_id) {
|
2016-03-25 15:37:16 +00:00
|
|
|
/*
|
2016-03-26 18:40:16 +00:00
|
|
|
* Make a note of any other host key formats that are available.
|
2016-03-25 15:37:16 +00:00
|
|
|
*/
|
|
|
|
{
|
2016-04-10 14:57:00 +00:00
|
|
|
int i, j, nkeys = 0;
|
2016-03-25 15:37:16 +00:00
|
|
|
char *list = NULL;
|
|
|
|
for (i = 0; i < lenof(hostkey_algs); i++) {
|
2018-06-03 11:58:05 +00:00
|
|
|
if (hostkey_algs[i].alg == ssh->hostkey_alg)
|
2016-03-26 18:40:16 +00:00
|
|
|
continue;
|
2016-03-27 13:59:18 +00:00
|
|
|
|
|
|
|
for (j = 0; j < ssh->n_uncert_hostkeys; j++)
|
|
|
|
if (ssh->uncert_hostkeys[j] == i)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (j < ssh->n_uncert_hostkeys) {
|
2016-03-25 15:37:16 +00:00
|
|
|
char *newlist;
|
|
|
|
if (list)
|
|
|
|
newlist = dupprintf("%s/%s", list,
|
2018-06-03 11:58:05 +00:00
|
|
|
hostkey_algs[i].alg->ssh_id);
|
2016-03-25 15:37:16 +00:00
|
|
|
else
|
2018-06-03 11:58:05 +00:00
|
|
|
newlist = dupprintf("%s", hostkey_algs[i].alg->ssh_id);
|
2016-03-25 15:37:16 +00:00
|
|
|
sfree(list);
|
|
|
|
list = newlist;
|
2016-04-10 14:57:00 +00:00
|
|
|
nkeys++;
|
2016-03-25 15:37:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (list) {
|
|
|
|
logeventf(ssh,
|
2016-03-26 18:40:16 +00:00
|
|
|
"Server also has %s host key%s, but we "
|
|
|
|
"don't know %s", list,
|
2016-04-10 14:57:00 +00:00
|
|
|
nkeys > 1 ? "s" : "",
|
|
|
|
nkeys > 1 ? "any of them" : "it");
|
2016-03-25 15:37:16 +00:00
|
|
|
sfree(list);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-18 06:48:20 +00:00
|
|
|
/*
|
|
|
|
* Authenticate remote host: verify host key. (We've already
|
|
|
|
* checked the signature of the exchange hash.)
|
|
|
|
*/
|
2018-06-03 11:58:05 +00:00
|
|
|
s->fingerprint = ssh2_fingerprint(s->hkey);
|
2013-08-18 06:48:20 +00:00
|
|
|
logevent("Host key fingerprint is:");
|
|
|
|
logevent(s->fingerprint);
|
New option to manually configure the expected host key(s).
This option is available from the command line as '-hostkey', and is
also configurable through the GUI. When enabled, it completely
replaces all of the automated host key management: the server's host
key will be checked against the manually configured list, and the
connection will be allowed or disconnected on that basis, and the host
key store in the registry will not be either consulted or updated.
The main aim is to provide a means of automatically running Plink,
PSCP or PSFTP deep inside Windows services where HKEY_CURRENT_USER
isn't available to have stored the right host key in. But it also
permits you to specify a list of multiple host keys, which means a
second use case for the same mechanism will probably be round-robin
DNS names that select one of several servers with different host keys.
Host keys can be specified as the standard MD5 fingerprint or as an
SSH-2 base64 blob, and are canonicalised on input. (The base64 blob is
more unwieldy, especially with Windows command-line length limits, but
provides a means of specifying the _whole_ public key in case you
don't trust MD5. I haven't bothered to provide an analogous mechanism
for SSH-1, on the basis that anyone worrying about MD5 should have
stopped using SSH-1 already!)
[originally from svn r10220]
2014-09-09 11:46:24 +00:00
|
|
|
/* First check against manually configured host keys. */
|
2018-06-03 11:58:05 +00:00
|
|
|
s->dlgret = verify_ssh_manual_host_key(ssh, s->fingerprint, s->hkey);
|
New option to manually configure the expected host key(s).
This option is available from the command line as '-hostkey', and is
also configurable through the GUI. When enabled, it completely
replaces all of the automated host key management: the server's host
key will be checked against the manually configured list, and the
connection will be allowed or disconnected on that basis, and the host
key store in the registry will not be either consulted or updated.
The main aim is to provide a means of automatically running Plink,
PSCP or PSFTP deep inside Windows services where HKEY_CURRENT_USER
isn't available to have stored the right host key in. But it also
permits you to specify a list of multiple host keys, which means a
second use case for the same mechanism will probably be round-robin
DNS names that select one of several servers with different host keys.
Host keys can be specified as the standard MD5 fingerprint or as an
SSH-2 base64 blob, and are canonicalised on input. (The base64 blob is
more unwieldy, especially with Windows command-line length limits, but
provides a means of specifying the _whole_ public key in case you
don't trust MD5. I haven't bothered to provide an analogous mechanism
for SSH-1, on the basis that anyone worrying about MD5 should have
stopped using SSH-1 already!)
[originally from svn r10220]
2014-09-09 11:46:24 +00:00
|
|
|
if (s->dlgret == 0) { /* did not match */
|
|
|
|
bombout(("Host key did not appear in manually configured list"));
|
|
|
|
crStopV;
|
|
|
|
} else if (s->dlgret < 0) { /* none configured; use standard handling */
|
|
|
|
s->dlgret = verify_ssh_host_key(ssh->frontend,
|
|
|
|
ssh->savedhost, ssh->savedport,
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh_key_cache_id(s->hkey),
|
|
|
|
s->keystr, s->fingerprint,
|
New option to manually configure the expected host key(s).
This option is available from the command line as '-hostkey', and is
also configurable through the GUI. When enabled, it completely
replaces all of the automated host key management: the server's host
key will be checked against the manually configured list, and the
connection will be allowed or disconnected on that basis, and the host
key store in the registry will not be either consulted or updated.
The main aim is to provide a means of automatically running Plink,
PSCP or PSFTP deep inside Windows services where HKEY_CURRENT_USER
isn't available to have stored the right host key in. But it also
permits you to specify a list of multiple host keys, which means a
second use case for the same mechanism will probably be round-robin
DNS names that select one of several servers with different host keys.
Host keys can be specified as the standard MD5 fingerprint or as an
SSH-2 base64 blob, and are canonicalised on input. (The base64 blob is
more unwieldy, especially with Windows command-line length limits, but
provides a means of specifying the _whole_ public key in case you
don't trust MD5. I haven't bothered to provide an analogous mechanism
for SSH-1, on the basis that anyone worrying about MD5 should have
stopped using SSH-1 already!)
[originally from svn r10220]
2014-09-09 11:46:24 +00:00
|
|
|
ssh_dialog_callback, ssh);
|
2015-10-18 12:04:58 +00:00
|
|
|
#ifdef FUZZING
|
|
|
|
s->dlgret = 1;
|
|
|
|
#endif
|
2018-05-18 06:22:58 +00:00
|
|
|
if (s->dlgret < 0) {
|
|
|
|
ssh->user_response = -1;
|
|
|
|
crWaitUntilV(ssh->user_response >= 0);
|
|
|
|
s->dlgret = ssh->user_response;
|
|
|
|
}
|
New option to manually configure the expected host key(s).
This option is available from the command line as '-hostkey', and is
also configurable through the GUI. When enabled, it completely
replaces all of the automated host key management: the server's host
key will be checked against the manually configured list, and the
connection will be allowed or disconnected on that basis, and the host
key store in the registry will not be either consulted or updated.
The main aim is to provide a means of automatically running Plink,
PSCP or PSFTP deep inside Windows services where HKEY_CURRENT_USER
isn't available to have stored the right host key in. But it also
permits you to specify a list of multiple host keys, which means a
second use case for the same mechanism will probably be round-robin
DNS names that select one of several servers with different host keys.
Host keys can be specified as the standard MD5 fingerprint or as an
SSH-2 base64 blob, and are canonicalised on input. (The base64 blob is
more unwieldy, especially with Windows command-line length limits, but
provides a means of specifying the _whole_ public key in case you
don't trust MD5. I haven't bothered to provide an analogous mechanism
for SSH-1, on the basis that anyone worrying about MD5 should have
stopped using SSH-1 already!)
[originally from svn r10220]
2014-09-09 11:46:24 +00:00
|
|
|
if (s->dlgret == 0) {
|
|
|
|
ssh_disconnect(ssh, "Aborted at host key verification", NULL,
|
|
|
|
0, TRUE);
|
|
|
|
crStopV;
|
|
|
|
}
|
|
|
|
}
|
2013-08-18 06:48:20 +00:00
|
|
|
sfree(s->fingerprint);
|
|
|
|
/*
|
|
|
|
* Save this host key, to check against the one presented in
|
|
|
|
* subsequent rekeys.
|
|
|
|
*/
|
|
|
|
ssh->hostkey_str = s->keystr;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
s->keystr = NULL;
|
Add manual cross-certification of new host keys.
If a server offers host key algorithms that we don't have a stored key
for, they will now appear in a submenu of the Special Commands menu.
Selecting one will force a repeat key exchange with that key, and if
it succeeds, will add the new host key to the cache. The idea is that
the new key sent by the server is protected by the crypto established
in the previous key exchange, so this is just as safe as typing some
command like 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub' at the
server prompt and transcribing the results manually.
This allows switching over to newer host key algorithms if the client
has begun to support them (e.g. people using PuTTY's new ECC
functionality for the first time), or if the server has acquired a new
key (e.g. due to a server OS upgrade).
At the moment, it's only available manually, for a single host key
type at a time. Automating it is potentially controversial for
security policy reasons (what if someone doesn't agree this is what
they want in their host key cache, or doesn't want to switch over to
using whichever of the keys PuTTY would now put top of the list?), for
code plumbing reasons (chaining several of these rekeys might be more
annoying than doing one at a time) and for CPU usage reasons (rekeys
are expensive), but even so, it might turn out to be a good idea in
future.
2016-03-21 07:25:31 +00:00
|
|
|
} else if (ssh->cross_certifying) {
|
2018-06-03 11:58:05 +00:00
|
|
|
s->fingerprint = ssh2_fingerprint(s->hkey);
|
Add manual cross-certification of new host keys.
If a server offers host key algorithms that we don't have a stored key
for, they will now appear in a submenu of the Special Commands menu.
Selecting one will force a repeat key exchange with that key, and if
it succeeds, will add the new host key to the cache. The idea is that
the new key sent by the server is protected by the crypto established
in the previous key exchange, so this is just as safe as typing some
command like 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub' at the
server prompt and transcribing the results manually.
This allows switching over to newer host key algorithms if the client
has begun to support them (e.g. people using PuTTY's new ECC
functionality for the first time), or if the server has acquired a new
key (e.g. due to a server OS upgrade).
At the moment, it's only available manually, for a single host key
type at a time. Automating it is potentially controversial for
security policy reasons (what if someone doesn't agree this is what
they want in their host key cache, or doesn't want to switch over to
using whichever of the keys PuTTY would now put top of the list?), for
code plumbing reasons (chaining several of these rekeys might be more
annoying than doing one at a time) and for CPU usage reasons (rekeys
are expensive), but even so, it might turn out to be a good idea in
future.
2016-03-21 07:25:31 +00:00
|
|
|
logevent("Storing additional host key for this host:");
|
|
|
|
logevent(s->fingerprint);
|
2017-01-06 10:44:47 +00:00
|
|
|
sfree(s->fingerprint);
|
Add manual cross-certification of new host keys.
If a server offers host key algorithms that we don't have a stored key
for, they will now appear in a submenu of the Special Commands menu.
Selecting one will force a repeat key exchange with that key, and if
it succeeds, will add the new host key to the cache. The idea is that
the new key sent by the server is protected by the crypto established
in the previous key exchange, so this is just as safe as typing some
command like 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub' at the
server prompt and transcribing the results manually.
This allows switching over to newer host key algorithms if the client
has begun to support them (e.g. people using PuTTY's new ECC
functionality for the first time), or if the server has acquired a new
key (e.g. due to a server OS upgrade).
At the moment, it's only available manually, for a single host key
type at a time. Automating it is potentially controversial for
security policy reasons (what if someone doesn't agree this is what
they want in their host key cache, or doesn't want to switch over to
using whichever of the keys PuTTY would now put top of the list?), for
code plumbing reasons (chaining several of these rekeys might be more
annoying than doing one at a time) and for CPU usage reasons (rekeys
are expensive), but even so, it might turn out to be a good idea in
future.
2016-03-21 07:25:31 +00:00
|
|
|
store_host_key(ssh->savedhost, ssh->savedport,
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh_key_cache_id(s->hkey), s->keystr);
|
Add manual cross-certification of new host keys.
If a server offers host key algorithms that we don't have a stored key
for, they will now appear in a submenu of the Special Commands menu.
Selecting one will force a repeat key exchange with that key, and if
it succeeds, will add the new host key to the cache. The idea is that
the new key sent by the server is protected by the crypto established
in the previous key exchange, so this is just as safe as typing some
command like 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub' at the
server prompt and transcribing the results manually.
This allows switching over to newer host key algorithms if the client
has begun to support them (e.g. people using PuTTY's new ECC
functionality for the first time), or if the server has acquired a new
key (e.g. due to a server OS upgrade).
At the moment, it's only available manually, for a single host key
type at a time. Automating it is potentially controversial for
security policy reasons (what if someone doesn't agree this is what
they want in their host key cache, or doesn't want to switch over to
using whichever of the keys PuTTY would now put top of the list?), for
code plumbing reasons (chaining several of these rekeys might be more
annoying than doing one at a time) and for CPU usage reasons (rekeys
are expensive), but even so, it might turn out to be a good idea in
future.
2016-03-21 07:25:31 +00:00
|
|
|
ssh->cross_certifying = FALSE;
|
|
|
|
/*
|
|
|
|
* Don't forget to store the new key as the one we'll be
|
|
|
|
* re-checking in future normal rekeys.
|
|
|
|
*/
|
|
|
|
ssh->hostkey_str = s->keystr;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
s->keystr = NULL;
|
2013-08-18 06:48:20 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* In a rekey, we never present an interactive host key
|
|
|
|
* verification request to the user. Instead, we simply
|
|
|
|
* enforce that the key we're seeing this time is identical to
|
|
|
|
* the one we saw before.
|
|
|
|
*/
|
|
|
|
if (strcmp(ssh->hostkey_str, s->keystr)) {
|
2015-10-18 12:04:58 +00:00
|
|
|
#ifndef FUZZING
|
2013-08-18 06:48:20 +00:00
|
|
|
bombout(("Host key was different in repeat key exchange"));
|
|
|
|
crStopV;
|
2015-10-18 12:04:58 +00:00
|
|
|
#endif
|
2013-08-18 06:48:20 +00:00
|
|
|
}
|
2001-01-26 18:50:13 +00:00
|
|
|
}
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
sfree(s->keystr);
|
|
|
|
if (s->hkey) {
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh_key_free(s->hkey);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
s->hkey = NULL;
|
|
|
|
}
|
2000-09-05 16:23:36 +00:00
|
|
|
|
2004-11-27 14:29:20 +00:00
|
|
|
/*
|
|
|
|
* The exchange hash from the very first key exchange is also
|
|
|
|
* the session id, used in session key construction and
|
|
|
|
* authentication.
|
|
|
|
*/
|
2004-12-31 10:51:14 +00:00
|
|
|
if (!s->got_session_id) {
|
2005-08-31 20:43:06 +00:00
|
|
|
assert(sizeof(s->exchange_hash) <= sizeof(ssh->v2_session_id));
|
2004-11-27 14:29:20 +00:00
|
|
|
memcpy(ssh->v2_session_id, s->exchange_hash,
|
|
|
|
sizeof(s->exchange_hash));
|
2005-08-31 20:43:06 +00:00
|
|
|
ssh->v2_session_id_len = ssh->kex->hash->hlen;
|
2005-09-02 10:51:09 +00:00
|
|
|
assert(ssh->v2_session_id_len <= sizeof(ssh->v2_session_id));
|
2004-12-31 10:51:14 +00:00
|
|
|
s->got_session_id = TRUE;
|
|
|
|
}
|
2004-11-27 14:29:20 +00:00
|
|
|
|
2000-09-05 16:23:36 +00:00
|
|
|
/*
|
2000-09-07 16:33:49 +00:00
|
|
|
* Send SSH2_MSG_NEWKEYS.
|
2000-09-05 16:23:36 +00:00
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_NEWKEYS);
|
|
|
|
ssh_pkt_write(ssh, s->pktout);
|
2004-11-27 14:29:20 +00:00
|
|
|
ssh->outgoing_data_size = 0; /* start counting from here */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We've sent client NEWKEYS, so create and initialise
|
2004-11-29 09:27:13 +00:00
|
|
|
* client-to-server session keys.
|
2004-11-27 14:29:20 +00:00
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
{
|
|
|
|
strbuf *cipher_key = strbuf_new();
|
|
|
|
strbuf *cipher_iv = strbuf_new();
|
|
|
|
strbuf *mac_key = strbuf_new();
|
|
|
|
|
|
|
|
if (s->out.cipher) {
|
|
|
|
ssh2_mkkey(ssh, cipher_iv, s->K, s->exchange_hash, 'A',
|
|
|
|
s->out.cipher->blksize);
|
|
|
|
ssh2_mkkey(ssh, cipher_key, s->K, s->exchange_hash, 'C',
|
|
|
|
s->out.cipher->padded_keybytes);
|
|
|
|
}
|
|
|
|
if (s->out.mac) {
|
|
|
|
ssh2_mkkey(ssh, mac_key, s->K, s->exchange_hash, 'E',
|
|
|
|
s->out.mac->keylen);
|
|
|
|
}
|
2004-11-27 14:29:20 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh2_bpp_new_outgoing_crypto(
|
|
|
|
ssh->bpp,
|
|
|
|
s->out.cipher, cipher_key->u, cipher_iv->u,
|
|
|
|
s->out.mac, s->out.etm_mode, mac_key->u,
|
|
|
|
s->out.comp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remember some details we'll need later for making other
|
|
|
|
* policy decisions based on the crypto we've just
|
|
|
|
* initialised.
|
|
|
|
*/
|
|
|
|
ssh->v2_cbc_ignore_workaround = (
|
|
|
|
s->out.cipher &&
|
|
|
|
(s->out.cipher->flags & SSH_CIPHER_IS_CBC) &&
|
|
|
|
!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE));
|
|
|
|
ssh->v2_out_cipherblksize = s->out.cipher->blksize;
|
|
|
|
|
|
|
|
strbuf_free(cipher_key);
|
|
|
|
strbuf_free(cipher_iv);
|
|
|
|
strbuf_free(mac_key);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->out.cipher)
|
|
|
|
logeventf(ssh, "Initialised %.200s client->server encryption",
|
|
|
|
s->out.cipher->text_name);
|
|
|
|
if (s->out.mac)
|
|
|
|
logeventf(ssh, "Initialised %.200s client->server"
|
|
|
|
" MAC algorithm%s%s",
|
|
|
|
s->out.mac->text_name,
|
|
|
|
s->out.etm_mode ? " (in ETM mode)" : "",
|
|
|
|
(s->out.cipher->required_mac ?
|
|
|
|
" (required by cipher)" : ""));
|
|
|
|
if (s->out.comp->text_name)
|
|
|
|
logeventf(ssh, "Initialised %s compression",
|
|
|
|
s->out.comp->text_name);
|
2004-11-24 19:23:02 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now our end of the key exchange is complete, we can send all
|
|
|
|
* our queued higher-layer packets.
|
|
|
|
*/
|
|
|
|
ssh->queueing = FALSE;
|
|
|
|
ssh2_pkt_queuesend(ssh);
|
2000-09-05 16:23:36 +00:00
|
|
|
|
2001-11-21 22:06:53 +00:00
|
|
|
/*
|
|
|
|
* Expect SSH2_MSG_NEWKEYS from server.
|
|
|
|
*/
|
2018-05-18 06:22:58 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL);
|
2004-11-24 18:45:52 +00:00
|
|
|
if (pktin->type != SSH2_MSG_NEWKEYS) {
|
2003-03-31 11:22:30 +00:00
|
|
|
bombout(("expected new-keys packet from server"));
|
2012-08-27 22:02:17 +00:00
|
|
|
crStopV;
|
2001-11-21 22:06:53 +00:00
|
|
|
}
|
2004-11-27 14:29:20 +00:00
|
|
|
ssh->incoming_data_size = 0; /* start counting from here */
|
2001-11-21 22:06:53 +00:00
|
|
|
|
2000-09-05 16:23:36 +00:00
|
|
|
/*
|
2004-11-27 14:29:20 +00:00
|
|
|
* We've seen server NEWKEYS, so create and initialise
|
|
|
|
* server-to-client session keys.
|
2000-09-05 16:23:36 +00:00
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
{
|
|
|
|
strbuf *cipher_key = strbuf_new();
|
|
|
|
strbuf *cipher_iv = strbuf_new();
|
|
|
|
strbuf *mac_key = strbuf_new();
|
|
|
|
|
|
|
|
if (s->in.cipher) {
|
|
|
|
ssh2_mkkey(ssh, cipher_iv, s->K, s->exchange_hash, 'B',
|
|
|
|
s->in.cipher->blksize);
|
|
|
|
ssh2_mkkey(ssh, cipher_key, s->K, s->exchange_hash, 'D',
|
|
|
|
s->in.cipher->padded_keybytes);
|
|
|
|
}
|
|
|
|
if (s->in.mac) {
|
|
|
|
ssh2_mkkey(ssh, mac_key, s->K, s->exchange_hash, 'F',
|
|
|
|
s->in.mac->keylen);
|
|
|
|
}
|
2002-10-25 12:51:28 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh2_bpp_new_incoming_crypto(
|
|
|
|
ssh->bpp,
|
|
|
|
s->in.cipher, cipher_key->u, cipher_iv->u,
|
|
|
|
s->in.mac, s->in.etm_mode, mac_key->u,
|
|
|
|
s->in.comp);
|
2002-10-25 12:51:28 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
strbuf_free(cipher_key);
|
|
|
|
strbuf_free(cipher_iv);
|
|
|
|
strbuf_free(mac_key);
|
|
|
|
}
|
2002-10-25 13:26:33 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
if (s->in.cipher)
|
|
|
|
logeventf(ssh, "Initialised %.200s server->client encryption",
|
|
|
|
s->in.cipher->text_name);
|
|
|
|
if (s->in.mac)
|
|
|
|
logeventf(ssh, "Initialised %.200s server->client"
|
|
|
|
" MAC algorithm%s%s",
|
|
|
|
s->in.mac->text_name,
|
|
|
|
s->in.etm_mode ? " (in ETM mode)" : "",
|
|
|
|
(s->in.cipher->required_mac ?
|
|
|
|
" (required by cipher)" : ""));
|
|
|
|
if (s->in.comp->text_name)
|
|
|
|
logeventf(ssh, "Initialised %s decompression",
|
|
|
|
s->in.comp->text_name);
|
2004-11-27 14:29:20 +00:00
|
|
|
|
|
|
|
/*
|
2007-04-30 22:09:26 +00:00
|
|
|
* Free shared secret.
|
2004-11-27 14:29:20 +00:00
|
|
|
*/
|
2003-12-19 12:44:46 +00:00
|
|
|
freebn(s->K);
|
2000-09-05 16:23:36 +00:00
|
|
|
|
2016-03-21 19:05:32 +00:00
|
|
|
/*
|
|
|
|
* Update the specials menu to list the remaining uncertified host
|
|
|
|
* keys.
|
|
|
|
*/
|
|
|
|
update_specials_menu(ssh->frontend);
|
|
|
|
|
2004-11-27 14:29:20 +00:00
|
|
|
/*
|
2004-12-31 10:51:14 +00:00
|
|
|
* Key exchange is over. Loop straight back round if we have a
|
|
|
|
* deferred rekey reason.
|
|
|
|
*/
|
|
|
|
if (ssh->deferred_rekey_reason) {
|
|
|
|
logevent(ssh->deferred_rekey_reason);
|
|
|
|
pktin = NULL;
|
|
|
|
ssh->deferred_rekey_reason = NULL;
|
|
|
|
goto begin_key_exchange;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Otherwise, schedule a timer for our next rekey.
|
2004-11-27 14:29:20 +00:00
|
|
|
*/
|
|
|
|
ssh->kex_in_progress = FALSE;
|
2004-12-28 14:10:32 +00:00
|
|
|
ssh->last_rekey = GETTICKCOUNT();
|
2018-04-14 13:48:02 +00:00
|
|
|
(void) ssh2_timer_update(ssh, 0);
|
2004-12-31 10:51:14 +00:00
|
|
|
|
2000-09-06 09:55:32 +00:00
|
|
|
/*
|
2018-05-18 06:22:58 +00:00
|
|
|
* Now we're encrypting. Get the next-layer protocol started if it
|
|
|
|
* hasn't already, and then sit here waiting for reasons to go
|
|
|
|
* back to the start and do a repeat key exchange. One of those
|
|
|
|
* reasons is that we receive KEXINIT from the other end; the
|
|
|
|
* other is if we find ssh->rekey_reason is non-NULL, i.e. we've
|
|
|
|
* decided to initiate a rekey ourselves for some reason.
|
2000-09-06 09:55:32 +00:00
|
|
|
*/
|
2018-05-18 12:46:36 +00:00
|
|
|
ssh->rekey_class = RK_NONE;
|
|
|
|
while (!pq_peek(&ssh->pq_ssh2_transport) && !ssh->rekey_class) {
|
2005-01-11 10:37:55 +00:00
|
|
|
wait_for_rekey:
|
2018-05-18 06:22:58 +00:00
|
|
|
if (!ssh->current_user_input_fn) {
|
2012-08-21 22:04:56 +00:00
|
|
|
/*
|
2018-05-18 06:22:59 +00:00
|
|
|
* Allow userauth to initialise itself.
|
2012-08-21 22:04:56 +00:00
|
|
|
*/
|
2018-05-18 06:22:59 +00:00
|
|
|
do_ssh2_userauth(ssh);
|
|
|
|
ssh->current_user_input_fn = ssh2_userauth_input;
|
2012-08-21 22:04:56 +00:00
|
|
|
}
|
2012-08-27 22:02:17 +00:00
|
|
|
crReturnV;
|
2001-02-20 13:55:59 +00:00
|
|
|
}
|
2018-05-18 06:22:58 +00:00
|
|
|
if ((pktin = pq_pop(&ssh->pq_ssh2_transport)) != NULL) {
|
|
|
|
if (pktin->type != SSH2_MSG_KEXINIT) {
|
|
|
|
bombout(("unexpected key exchange packet, type %d", pktin->type));
|
|
|
|
crStopV;
|
|
|
|
}
|
2004-11-27 14:29:20 +00:00
|
|
|
logevent("Server initiated key re-exchange");
|
|
|
|
} else {
|
2018-05-18 06:22:58 +00:00
|
|
|
if (ssh->rekey_class == RK_POST_USERAUTH) {
|
2011-03-04 22:34:47 +00:00
|
|
|
/*
|
2018-05-18 06:22:59 +00:00
|
|
|
* userauth has seen a USERAUTH_SUCCEEDED. For a couple of
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
* reasons, this may be the moment to do an immediate
|
|
|
|
* rekey with different parameters.
|
2011-03-04 22:34:47 +00:00
|
|
|
*
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
* One is to turn on delayed compression. We do this by a
|
|
|
|
* rekey to work around a protocol design bug:
|
2011-03-04 22:34:47 +00:00
|
|
|
* draft-miller-secsh-compression-delayed-00 says that you
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
* negotiate delayed compression in the first key
|
|
|
|
* exchange, and both sides start compressing when the
|
|
|
|
* server has sent USERAUTH_SUCCESS. This has a race
|
|
|
|
* condition -- the server can't know when the client has
|
|
|
|
* seen it, and thus which incoming packets it should
|
|
|
|
* treat as compressed.
|
2011-03-04 22:34:47 +00:00
|
|
|
*
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
* Instead, we do the initial key exchange without
|
|
|
|
* offering the delayed methods, but note if the server
|
|
|
|
* offers them; when we get here, if a delayed method was
|
|
|
|
* available that was higher on our list than what we got,
|
|
|
|
* we initiate a rekey in which we _do_ list the delayed
|
|
|
|
* methods (and hopefully get it as a result). Subsequent
|
|
|
|
* rekeys will do the same.
|
|
|
|
*
|
|
|
|
* Another reason for a rekey at this point is if we've
|
|
|
|
* done a GSS key exchange and don't have anything in our
|
|
|
|
* transient hostkey cache, in which case we should make
|
|
|
|
* an attempt to populate the cache now.
|
2011-03-04 22:34:47 +00:00
|
|
|
*/
|
|
|
|
assert(!s->userauth_succeeded); /* should only happen once */
|
|
|
|
s->userauth_succeeded = TRUE;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
if (s->pending_compression) {
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->rekey_reason = "enabling delayed compression";
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
} else if (s->need_gss_transient_hostkey) {
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->rekey_reason = "populating transient host key cache";
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
} else {
|
2011-03-04 22:34:47 +00:00
|
|
|
/* Can't see any point rekeying. */
|
|
|
|
goto wait_for_rekey; /* this is utterly horrid */
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
}
|
2011-03-04 22:34:47 +00:00
|
|
|
/* else fall through to rekey... */
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
|
2011-03-04 22:34:47 +00:00
|
|
|
s->pending_compression = FALSE;
|
|
|
|
}
|
2005-01-11 10:37:55 +00:00
|
|
|
/*
|
2011-03-04 22:34:47 +00:00
|
|
|
* Now we've decided to rekey.
|
|
|
|
*
|
2005-01-11 10:37:55 +00:00
|
|
|
* Special case: if the server bug is set that doesn't
|
|
|
|
* allow rekeying, we give a different log message and
|
|
|
|
* continue waiting. (If such a server _initiates_ a rekey,
|
|
|
|
* we process it anyway!)
|
|
|
|
*/
|
|
|
|
if ((ssh->remote_bugs & BUG_SSH2_REKEY)) {
|
|
|
|
logeventf(ssh, "Server bug prevents key re-exchange (%s)",
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->rekey_reason);
|
2005-01-11 10:37:55 +00:00
|
|
|
/* Reset the counters, so that at least this message doesn't
|
|
|
|
* hit the event log _too_ often. */
|
|
|
|
ssh->outgoing_data_size = 0;
|
|
|
|
ssh->incoming_data_size = 0;
|
2018-04-14 13:48:02 +00:00
|
|
|
(void) ssh2_timer_update(ssh, 0);
|
2011-03-04 22:34:47 +00:00
|
|
|
goto wait_for_rekey; /* this is still utterly horrid */
|
2005-01-11 10:37:55 +00:00
|
|
|
} else {
|
2018-05-18 06:22:58 +00:00
|
|
|
logeventf(ssh, "Initiating key re-exchange (%s)",
|
|
|
|
ssh->rekey_reason);
|
2005-01-11 10:37:55 +00:00
|
|
|
}
|
2004-11-27 14:29:20 +00:00
|
|
|
}
|
2000-09-07 16:33:49 +00:00
|
|
|
goto begin_key_exchange;
|
2000-09-05 14:28:17 +00:00
|
|
|
|
2012-08-27 22:02:17 +00:00
|
|
|
crFinishV;
|
2000-09-05 14:28:17 +00:00
|
|
|
}
|
|
|
|
|
2001-01-22 15:36:07 +00:00
|
|
|
/*
|
2016-04-23 14:51:45 +00:00
|
|
|
* Send data on an SSH channel. In SSH-2, this involves buffering it
|
|
|
|
* first.
|
2001-01-22 15:36:07 +00:00
|
|
|
*/
|
2016-04-23 14:51:45 +00:00
|
|
|
static int ssh_send_channel_data(struct ssh_channel *c, const char *buf,
|
2016-04-23 12:17:33 +00:00
|
|
|
int len)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
assert(!(c->closes & CLOSES_SENT_EOF));
|
|
|
|
|
2016-04-23 14:51:45 +00:00
|
|
|
if (c->ssh->version == 2) {
|
|
|
|
bufchain_add(&c->v.v2.outbuffer, buf, len);
|
|
|
|
return ssh2_try_send(c);
|
|
|
|
} else {
|
2018-06-09 08:59:58 +00:00
|
|
|
PktOut *pkt = ssh_bpp_new_pktout(c->ssh->bpp, SSH1_MSG_CHANNEL_DATA);
|
|
|
|
put_uint32(pkt, c->remoteid);
|
|
|
|
put_string(pkt, buf, len);
|
|
|
|
ssh_pkt_write(c->ssh, pkt);
|
2016-04-23 14:51:45 +00:00
|
|
|
/*
|
|
|
|
* In SSH-1 we can return 0 here - implying that channels are
|
|
|
|
* never individually throttled - because the only
|
|
|
|
* circumstance that can cause throttling will be the whole
|
|
|
|
* SSH connection backing up, in which case _everything_ will
|
|
|
|
* be throttled as a whole.
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
2001-01-22 15:36:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-03-10 16:36:05 +00:00
|
|
|
* Attempt to send data on an SSH-2 channel.
|
2001-01-22 15:36:07 +00:00
|
|
|
*/
|
2001-08-25 17:09:23 +00:00
|
|
|
static int ssh2_try_send(struct ssh_channel *c)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2002-10-25 11:30:33 +00:00
|
|
|
Ssh ssh = c->ssh;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
2011-09-13 11:44:03 +00:00
|
|
|
int ret;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
2001-08-25 17:09:23 +00:00
|
|
|
while (c->v.v2.remwindow > 0 && bufchain_size(&c->v.v2.outbuffer) > 0) {
|
|
|
|
int len;
|
|
|
|
void *data;
|
|
|
|
bufchain_prefix(&c->v.v2.outbuffer, &data, &len);
|
|
|
|
if ((unsigned)len > c->v.v2.remwindow)
|
|
|
|
len = c->v.v2.remwindow;
|
|
|
|
if ((unsigned)len > c->v.v2.remmaxpkt)
|
|
|
|
len = c->v.v2.remmaxpkt;
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_CHANNEL_DATA);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(pktout, c->remoteid);
|
|
|
|
put_string(pktout, data, len);
|
2004-11-24 18:45:52 +00:00
|
|
|
ssh2_pkt_send(ssh, pktout);
|
2017-11-26 19:59:27 +00:00
|
|
|
if (!ssh->s) /* a network error might have closed the socket */
|
|
|
|
break;
|
2001-08-25 17:09:23 +00:00
|
|
|
bufchain_consume(&c->v.v2.outbuffer, len);
|
|
|
|
c->v.v2.remwindow -= len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* After having sent as much data as we can, return the amount
|
|
|
|
* still buffered.
|
|
|
|
*/
|
2011-09-13 11:44:03 +00:00
|
|
|
ret = bufchain_size(&c->v.v2.outbuffer);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* And if there's no data pending but we need to send an EOF, send
|
|
|
|
* it.
|
|
|
|
*/
|
|
|
|
if (!ret && c->pending_eof)
|
|
|
|
ssh_channel_try_eof(c);
|
|
|
|
|
|
|
|
return ret;
|
2001-08-25 17:09:23 +00:00
|
|
|
}
|
|
|
|
|
2010-07-04 22:53:53 +00:00
|
|
|
static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c)
|
2005-03-17 19:49:51 +00:00
|
|
|
{
|
|
|
|
int bufsize;
|
2011-09-13 11:44:03 +00:00
|
|
|
if (c->closes & CLOSES_SENT_EOF)
|
|
|
|
return; /* don't send on channels we've EOFed */
|
2005-03-17 19:49:51 +00:00
|
|
|
bufsize = ssh2_try_send(c);
|
|
|
|
if (bufsize == 0) {
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
c->throttled_by_backlog = FALSE;
|
|
|
|
ssh_channel_check_throttle(c);
|
2005-03-17 19:49:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-17 14:05:41 +00:00
|
|
|
static int ssh_is_simple(Ssh ssh)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We use the 'simple' variant of the SSH protocol if we're asked
|
|
|
|
* to, except not if we're also doing connection-sharing (either
|
|
|
|
* tunnelling our packets over an upstream or expecting to be
|
|
|
|
* tunnelled over ourselves), since then the assumption that we
|
|
|
|
* have only one channel to worry about is not true after all.
|
|
|
|
*/
|
|
|
|
return (conf_get_int(ssh->conf, CONF_ssh_simple) &&
|
|
|
|
!ssh->bare_connection && !ssh->connshare);
|
|
|
|
}
|
|
|
|
|
2007-10-03 21:04:26 +00:00
|
|
|
/*
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
* Set up most of a new ssh_channel. Nulls out sharectx, but leaves
|
|
|
|
* chan untouched (since it will sometimes have been filled in before
|
|
|
|
* calling this).
|
2007-10-03 21:04:26 +00:00
|
|
|
*/
|
2016-05-21 21:29:57 +00:00
|
|
|
static void ssh_channel_init(struct ssh_channel *c)
|
2007-10-03 21:04:26 +00:00
|
|
|
{
|
|
|
|
Ssh ssh = c->ssh;
|
|
|
|
c->localid = alloc_channel_id(ssh);
|
|
|
|
c->closes = 0;
|
2011-09-13 11:44:03 +00:00
|
|
|
c->pending_eof = FALSE;
|
2007-10-03 21:04:26 +00:00
|
|
|
c->throttling_conn = FALSE;
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
c->sharectx = NULL;
|
2016-05-21 21:29:57 +00:00
|
|
|
if (ssh->version == 2) {
|
|
|
|
c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin =
|
|
|
|
ssh_is_simple(ssh) ? OUR_V2_BIGWIN : OUR_V2_WINSIZE;
|
|
|
|
c->v.v2.chanreq_head = NULL;
|
|
|
|
c->v.v2.throttle_state = UNTHROTTLED;
|
|
|
|
bufchain_init(&c->v.v2.outbuffer);
|
|
|
|
}
|
2018-09-14 12:47:13 +00:00
|
|
|
c->sc.vt = &sshchannel_vtable;
|
2016-05-21 21:58:57 +00:00
|
|
|
add234(ssh->channels, c);
|
2007-10-03 21:04:26 +00:00
|
|
|
}
|
|
|
|
|
2012-09-01 12:28:38 +00:00
|
|
|
/*
|
|
|
|
* Construct the common parts of a CHANNEL_OPEN.
|
|
|
|
*/
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static PktOut *ssh2_chanopen_init(struct ssh_channel *c,
|
2015-05-15 10:15:42 +00:00
|
|
|
const char *type)
|
2012-09-01 12:28:38 +00:00
|
|
|
{
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
2012-09-01 12:28:38 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
pktout = ssh_bpp_new_pktout(c->ssh->bpp, SSH2_MSG_CHANNEL_OPEN);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(pktout, type);
|
|
|
|
put_uint32(pktout, c->localid);
|
|
|
|
put_uint32(pktout, c->v.v2.locwindow);/* our window size */
|
|
|
|
put_uint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */
|
2012-09-01 12:28:38 +00:00
|
|
|
return pktout;
|
|
|
|
}
|
|
|
|
|
2012-08-25 11:12:14 +00:00
|
|
|
/*
|
|
|
|
* CHANNEL_FAILURE doesn't come with any indication of what message
|
|
|
|
* caused it, so we have to keep track of the outstanding
|
|
|
|
* CHANNEL_REQUESTs ourselves.
|
|
|
|
*/
|
|
|
|
static void ssh2_queue_chanreq_handler(struct ssh_channel *c,
|
|
|
|
cchandler_fn_t handler, void *ctx)
|
|
|
|
{
|
|
|
|
struct outstanding_channel_request *ocr =
|
|
|
|
snew(struct outstanding_channel_request);
|
|
|
|
|
2012-08-25 21:06:48 +00:00
|
|
|
assert(!(c->closes & (CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE)));
|
2012-08-25 11:12:14 +00:00
|
|
|
ocr->handler = handler;
|
|
|
|
ocr->ctx = ctx;
|
|
|
|
ocr->next = NULL;
|
|
|
|
if (!c->v.v2.chanreq_head)
|
|
|
|
c->v.v2.chanreq_head = ocr;
|
|
|
|
else
|
|
|
|
c->v.v2.chanreq_tail->next = ocr;
|
|
|
|
c->v.v2.chanreq_tail = ocr;
|
|
|
|
}
|
|
|
|
|
2012-08-25 15:57:05 +00:00
|
|
|
/*
|
|
|
|
* Construct the common parts of a CHANNEL_REQUEST. If handler is not
|
|
|
|
* NULL then a reply will be requested and the handler will be called
|
|
|
|
* when it arrives. The returned packet is ready to have any
|
|
|
|
* request-specific data added and be sent. Note that if a handler is
|
|
|
|
* provided, it's essential that the request actually be sent.
|
2012-08-25 21:06:48 +00:00
|
|
|
*
|
2014-07-06 14:05:39 +00:00
|
|
|
* The handler will usually be passed the response packet in pktin. If
|
|
|
|
* pktin is NULL, this means that no reply will ever be forthcoming
|
|
|
|
* (e.g. because the entire connection is being destroyed, or because
|
|
|
|
* the server initiated channel closure before we saw the response)
|
|
|
|
* and the handler should free any storage it's holding.
|
2012-08-25 15:57:05 +00:00
|
|
|
*/
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static PktOut *ssh2_chanreq_init(struct ssh_channel *c,
|
2015-05-15 10:15:42 +00:00
|
|
|
const char *type,
|
2012-08-25 15:57:05 +00:00
|
|
|
cchandler_fn_t handler, void *ctx)
|
|
|
|
{
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
2012-08-25 15:57:05 +00:00
|
|
|
|
2012-08-25 21:06:48 +00:00
|
|
|
assert(!(c->closes & (CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE)));
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
pktout = ssh_bpp_new_pktout(c->ssh->bpp, SSH2_MSG_CHANNEL_REQUEST);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(pktout, c->remoteid);
|
|
|
|
put_stringz(pktout, type);
|
|
|
|
put_bool(pktout, handler != NULL);
|
2012-08-25 15:57:05 +00:00
|
|
|
if (handler != NULL)
|
|
|
|
ssh2_queue_chanreq_handler(c, handler, ctx);
|
|
|
|
return pktout;
|
|
|
|
}
|
|
|
|
|
2016-05-17 13:57:51 +00:00
|
|
|
static void ssh_channel_unthrottle(struct ssh_channel *c, int bufsize)
|
|
|
|
{
|
|
|
|
Ssh ssh = c->ssh;
|
|
|
|
int buflimit;
|
|
|
|
|
|
|
|
if (ssh->version == 1) {
|
|
|
|
buflimit = SSH1_BUFFER_LIMIT;
|
|
|
|
} else {
|
|
|
|
if (ssh_is_simple(ssh))
|
|
|
|
buflimit = 0;
|
|
|
|
else
|
|
|
|
buflimit = c->v.v2.locmaxwin;
|
2016-05-17 14:28:56 +00:00
|
|
|
if (bufsize < buflimit)
|
|
|
|
ssh2_set_window(c, buflimit - bufsize);
|
2016-05-17 13:57:51 +00:00
|
|
|
}
|
|
|
|
if (c->throttling_conn && bufsize <= buflimit) {
|
|
|
|
c->throttling_conn = 0;
|
|
|
|
ssh_throttle_conn(ssh, -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-08-25 17:09:23 +00:00
|
|
|
/*
|
2005-03-10 16:36:05 +00:00
|
|
|
* Potentially enlarge the window on an SSH-2 channel.
|
2001-08-25 17:09:23 +00:00
|
|
|
*/
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_handle_winadj_response(struct ssh_channel *, PktIn *,
|
2012-08-25 11:12:14 +00:00
|
|
|
void *);
|
2007-08-06 20:56:52 +00:00
|
|
|
static void ssh2_set_window(struct ssh_channel *c, int newwin)
|
2001-08-25 17:09:23 +00:00
|
|
|
{
|
2002-10-25 11:30:33 +00:00
|
|
|
Ssh ssh = c->ssh;
|
|
|
|
|
2002-01-10 13:29:43 +00:00
|
|
|
/*
|
2011-09-13 11:44:03 +00:00
|
|
|
* Never send WINDOW_ADJUST for a channel that the remote side has
|
|
|
|
* already sent EOF on; there's no point, since it won't be
|
2011-12-08 19:15:53 +00:00
|
|
|
* sending any more data anyway. Ditto if _we've_ already sent
|
|
|
|
* CLOSE.
|
2002-01-10 13:29:43 +00:00
|
|
|
*/
|
2011-12-08 19:15:53 +00:00
|
|
|
if (c->closes & (CLOSES_RCVD_EOF | CLOSES_SENT_CLOSE))
|
2002-01-10 13:29:43 +00:00
|
|
|
return;
|
|
|
|
|
2013-11-17 14:05:41 +00:00
|
|
|
/*
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
* If the client-side Channel is in an initial setup phase with a
|
|
|
|
* fixed window size, e.g. for an X11 channel when we're still
|
|
|
|
* waiting to see its initial auth and may yet hand it off to a
|
|
|
|
* downstream, don't send any WINDOW_ADJUST either.
|
2013-11-17 14:05:41 +00:00
|
|
|
*/
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
if (c->chan->initial_fixed_window_size)
|
2013-11-17 14:05:41 +00:00
|
|
|
return;
|
|
|
|
|
2005-01-16 14:43:12 +00:00
|
|
|
/*
|
2007-12-05 00:02:06 +00:00
|
|
|
* If the remote end has a habit of ignoring maxpkt, limit the
|
|
|
|
* window so that it has no choice (assuming it doesn't ignore the
|
|
|
|
* window as well).
|
|
|
|
*/
|
|
|
|
if ((ssh->remote_bugs & BUG_SSH2_MAXPKT) && newwin > OUR_V2_MAXPKT)
|
|
|
|
newwin = OUR_V2_MAXPKT;
|
|
|
|
|
|
|
|
/*
|
2005-01-16 14:43:12 +00:00
|
|
|
* Only send a WINDOW_ADJUST if there's significantly more window
|
|
|
|
* available than the other end thinks there is. This saves us
|
|
|
|
* sending a WINDOW_ADJUST for every character in a shell session.
|
|
|
|
*
|
|
|
|
* "Significant" is arbitrarily defined as half the window size.
|
|
|
|
*/
|
2007-08-05 14:18:43 +00:00
|
|
|
if (newwin / 2 >= c->v.v2.locwindow) {
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
2012-08-25 11:12:14 +00:00
|
|
|
unsigned *up;
|
2004-11-24 18:45:52 +00:00
|
|
|
|
2007-09-24 15:18:11 +00:00
|
|
|
/*
|
|
|
|
* In order to keep track of how much window the client
|
|
|
|
* actually has available, we'd like it to acknowledge each
|
|
|
|
* WINDOW_ADJUST. We can't do that directly, so we accompany
|
|
|
|
* it with a CHANNEL_REQUEST that has to be acknowledged.
|
|
|
|
*
|
|
|
|
* This is only necessary if we're opening the window wide.
|
|
|
|
* If we're not, then throughput is being constrained by
|
|
|
|
* something other than the maximum window size anyway.
|
|
|
|
*/
|
|
|
|
if (newwin == c->v.v2.locmaxwin &&
|
2012-07-28 19:30:12 +00:00
|
|
|
!(ssh->remote_bugs & BUG_CHOKES_ON_WINADJ)) {
|
2012-08-25 11:12:14 +00:00
|
|
|
up = snew(unsigned);
|
|
|
|
*up = newwin - c->v.v2.locwindow;
|
2012-08-25 15:57:05 +00:00
|
|
|
pktout = ssh2_chanreq_init(c, "winadj@putty.projects.tartarus.org",
|
|
|
|
ssh2_handle_winadj_response, up);
|
|
|
|
ssh2_pkt_send(ssh, pktout);
|
|
|
|
|
2007-09-24 15:18:11 +00:00
|
|
|
if (c->v.v2.throttle_state != UNTHROTTLED)
|
|
|
|
c->v.v2.throttle_state = UNTHROTTLING;
|
|
|
|
} else {
|
|
|
|
/* Pretend the WINDOW_ADJUST was acked immediately. */
|
|
|
|
c->v.v2.remlocwin = newwin;
|
|
|
|
c->v.v2.throttle_state = THROTTLED;
|
|
|
|
}
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_CHANNEL_WINDOW_ADJUST);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(pktout, c->remoteid);
|
|
|
|
put_uint32(pktout, newwin - c->v.v2.locwindow);
|
2004-11-24 18:45:52 +00:00
|
|
|
ssh2_pkt_send(ssh, pktout);
|
2001-08-25 17:09:23 +00:00
|
|
|
c->v.v2.locwindow = newwin;
|
2001-01-22 15:36:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-30 19:42:31 +00:00
|
|
|
/*
|
|
|
|
* Find the channel associated with a message. If there's no channel,
|
|
|
|
* or it's not properly open, make a noise about it and return NULL.
|
2016-05-22 22:59:48 +00:00
|
|
|
* If the channel is shared, pass the message on to downstream and
|
|
|
|
* also return NULL (meaning the caller should ignore this message).
|
2007-09-30 19:42:31 +00:00
|
|
|
*/
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static struct ssh_channel *ssh_channel_msg(Ssh ssh, PktIn *pktin)
|
2007-09-30 19:42:31 +00:00
|
|
|
{
|
2018-05-27 17:13:53 +00:00
|
|
|
unsigned localid = get_uint32(pktin);
|
2007-09-30 19:42:31 +00:00
|
|
|
struct ssh_channel *c;
|
2016-05-22 21:14:00 +00:00
|
|
|
int halfopen_ok;
|
2007-09-30 19:42:31 +00:00
|
|
|
|
2016-05-22 21:14:00 +00:00
|
|
|
/* Is this message OK on a half-open connection? */
|
|
|
|
if (ssh->version == 1)
|
|
|
|
halfopen_ok = (pktin->type == SSH1_MSG_CHANNEL_OPEN_CONFIRMATION ||
|
|
|
|
pktin->type == SSH1_MSG_CHANNEL_OPEN_FAILURE);
|
|
|
|
else
|
|
|
|
halfopen_ok = (pktin->type == SSH2_MSG_CHANNEL_OPEN_CONFIRMATION ||
|
|
|
|
pktin->type == SSH2_MSG_CHANNEL_OPEN_FAILURE);
|
2007-09-30 19:42:31 +00:00
|
|
|
c = find234(ssh->channels, &localid, ssh_channelfind);
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
if (c && c->sharectx) {
|
|
|
|
share_got_pkt_from_server(c->sharectx, pktin->type,
|
|
|
|
BinarySource_UPCAST(pktin)->data,
|
|
|
|
BinarySource_UPCAST(pktin)->len);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (!c || c->halfopen != halfopen_ok) {
|
2007-09-30 19:42:31 +00:00
|
|
|
char *buf = dupprintf("Received %s for %s channel %u",
|
2016-05-22 21:14:00 +00:00
|
|
|
ssh_pkt_type(ssh, pktin->type),
|
2016-05-22 21:57:25 +00:00
|
|
|
!c ? "nonexistent" :
|
|
|
|
c->halfopen ? "half-open" : "open",
|
|
|
|
localid);
|
2007-09-30 19:42:31 +00:00
|
|
|
ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
|
|
|
|
sfree(buf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2012-08-25 11:12:14 +00:00
|
|
|
static void ssh2_handle_winadj_response(struct ssh_channel *c,
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktIn *pktin, void *ctx)
|
2011-07-01 21:53:41 +00:00
|
|
|
{
|
2012-08-25 11:12:14 +00:00
|
|
|
unsigned *sizep = ctx;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Winadj responses should always be failures. However, at least
|
|
|
|
* one server ("boks_sshd") is known to return SUCCESS for channel
|
|
|
|
* requests it's never heard of, such as "winadj@putty". Raised
|
|
|
|
* with foxt.com as bug 090916-090424, but for the sake of a quiet
|
|
|
|
* life, we don't worry about what kind of response we got.
|
|
|
|
*/
|
|
|
|
|
|
|
|
c->v.v2.remlocwin += *sizep;
|
|
|
|
sfree(sizep);
|
2011-07-01 21:53:41 +00:00
|
|
|
/*
|
|
|
|
* winadj messages are only sent when the window is fully open, so
|
|
|
|
* if we get an ack of one, we know any pending unthrottle is
|
|
|
|
* complete.
|
|
|
|
*/
|
|
|
|
if (c->v.v2.throttle_state == UNTHROTTLING)
|
|
|
|
c->v.v2.throttle_state = UNTHROTTLED;
|
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_channel_response(Ssh ssh, PktIn *pktin)
|
2007-10-01 21:11:11 +00:00
|
|
|
{
|
2016-05-22 21:14:00 +00:00
|
|
|
struct ssh_channel *c = ssh_channel_msg(ssh, pktin);
|
2012-08-25 11:12:14 +00:00
|
|
|
struct outstanding_channel_request *ocr;
|
2007-10-01 21:11:11 +00:00
|
|
|
|
2012-08-25 11:12:14 +00:00
|
|
|
if (!c) return;
|
|
|
|
ocr = c->v.v2.chanreq_head;
|
|
|
|
if (!ocr) {
|
|
|
|
ssh2_msg_unexpected(ssh, pktin);
|
2007-10-01 21:11:11 +00:00
|
|
|
return;
|
2012-08-25 11:12:14 +00:00
|
|
|
}
|
|
|
|
ocr->handler(c, pktin, ocr->ctx);
|
Stop ssh2_msg_channel_response using a stale ssh_channel.
When it calls through ocr->handler() to process the response to a
channel request, sometimes that call ends up back in the main SSH-2
authconn coroutine, and sometimes _that_ will call bomb_out(), which
closes the whole SSH connection and frees all the channels - so that
when control returns back up the call stack to
ssh2_msg_channel_response itself which continues working with the
channel it was passed, it's using freed memory and things go badly.
This is the sort of thing I'd _like_ to fix using some kind of
large-scale refactoring along the lines of moving all the actual
free() calls out into top-level callbacks, so that _any_ function
which is holding a pointer to something can rely on that pointer still
being valid after it calls a subroutine. But I haven't worked out all
the details of how that system should work, and doubtless it will turn
out to have problems of its own once I do, so here's a point fix which
simply checks if the whole SSH session has been closed (which is easy
- much easier than checking if that _channel_ structure still exists)
and fixes the immediate bug.
(I think this is the real fix for the problem reported by the user I
mention in commit f0126dd19, because I actually got the details wrong
in the log message for that previous commit: the user's SSH server
wasn't rejecting the _opening_ of the main session channel, it was
rejecting the "shell" channel request, so this code path was the one
being exercised. Still, the other bug was real too, so no harm done!)
2017-07-19 06:22:03 +00:00
|
|
|
if (ssh->state == SSH_STATE_CLOSED)
|
|
|
|
return; /* in case the handler called bomb_out(), which some can */
|
2012-08-25 11:12:14 +00:00
|
|
|
c->v.v2.chanreq_head = ocr->next;
|
|
|
|
sfree(ocr);
|
2007-09-24 15:18:11 +00:00
|
|
|
/*
|
2012-08-25 11:12:14 +00:00
|
|
|
* We may now initiate channel-closing procedures, if that
|
|
|
|
* CHANNEL_REQUEST was the last thing outstanding before we send
|
|
|
|
* CHANNEL_CLOSE.
|
2007-09-24 15:18:11 +00:00
|
|
|
*/
|
2012-08-25 11:12:14 +00:00
|
|
|
ssh2_channel_check_close(c);
|
2007-09-24 15:18:11 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_channel_window_adjust(Ssh ssh, PktIn *pktin)
|
2004-11-24 20:35:15 +00:00
|
|
|
{
|
|
|
|
struct ssh_channel *c;
|
2016-05-22 21:14:00 +00:00
|
|
|
c = ssh_channel_msg(ssh, pktin);
|
2007-09-30 19:42:31 +00:00
|
|
|
if (!c)
|
|
|
|
return;
|
2011-09-13 11:44:03 +00:00
|
|
|
if (!(c->closes & CLOSES_SENT_EOF)) {
|
2018-05-27 17:13:53 +00:00
|
|
|
c->v.v2.remwindow += get_uint32(pktin);
|
2010-07-04 22:53:53 +00:00
|
|
|
ssh2_try_send_and_unthrottle(ssh, c);
|
2005-03-17 19:49:51 +00:00
|
|
|
}
|
2004-11-24 20:35:15 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_channel_data(Ssh ssh, PktIn *pktin)
|
2004-11-27 15:32:45 +00:00
|
|
|
{
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen data;
|
2016-05-24 21:38:40 +00:00
|
|
|
unsigned ext_type = 0; /* 0 means not extended */
|
2004-11-27 15:32:45 +00:00
|
|
|
struct ssh_channel *c;
|
2016-05-22 21:14:00 +00:00
|
|
|
c = ssh_channel_msg(ssh, pktin);
|
2004-11-27 15:32:45 +00:00
|
|
|
if (!c)
|
2007-09-30 19:42:31 +00:00
|
|
|
return;
|
2016-05-24 21:38:40 +00:00
|
|
|
if (pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)
|
2018-05-27 17:13:53 +00:00
|
|
|
ext_type = get_uint32(pktin);
|
|
|
|
data = get_string(pktin);
|
|
|
|
if (!get_err(pktin)) {
|
2016-05-21 12:13:00 +00:00
|
|
|
int bufsize;
|
2018-05-27 17:13:53 +00:00
|
|
|
c->v.v2.locwindow -= data.len;
|
|
|
|
c->v.v2.remlocwin -= data.len;
|
2016-05-24 21:38:40 +00:00
|
|
|
if (ext_type != 0 && ext_type != SSH2_EXTENDED_DATA_STDERR)
|
2018-05-27 17:13:53 +00:00
|
|
|
data.len = 0; /* Don't do anything with unknown extended data. */
|
2016-05-24 21:38:40 +00:00
|
|
|
bufsize = ssh_channel_data(c, ext_type == SSH2_EXTENDED_DATA_STDERR,
|
2018-05-27 17:13:53 +00:00
|
|
|
data.ptr, data.len);
|
2007-09-24 15:18:11 +00:00
|
|
|
/*
|
|
|
|
* If it looks like the remote end hit the end of its window,
|
|
|
|
* and we didn't want it to do that, think about using a
|
|
|
|
* larger window.
|
|
|
|
*/
|
|
|
|
if (c->v.v2.remlocwin <= 0 && c->v.v2.throttle_state == UNTHROTTLED &&
|
|
|
|
c->v.v2.locmaxwin < 0x40000000)
|
|
|
|
c->v.v2.locmaxwin += OUR_V2_WINSIZE;
|
2004-11-27 15:32:45 +00:00
|
|
|
/*
|
|
|
|
* If we are not buffering too much data,
|
|
|
|
* enlarge the window again at the remote side.
|
2007-08-04 14:32:06 +00:00
|
|
|
* If we are buffering too much, we may still
|
|
|
|
* need to adjust the window if the server's
|
|
|
|
* sent excess data.
|
2004-11-27 15:32:45 +00:00
|
|
|
*/
|
2016-05-17 14:28:56 +00:00
|
|
|
if (bufsize < c->v.v2.locmaxwin)
|
|
|
|
ssh2_set_window(c, c->v.v2.locmaxwin - bufsize);
|
2007-10-03 20:29:27 +00:00
|
|
|
/*
|
|
|
|
* If we're either buffering way too much data, or if we're
|
|
|
|
* buffering anything at all and we're in "simple" mode,
|
|
|
|
* throttle the whole channel.
|
|
|
|
*/
|
2013-11-17 14:05:41 +00:00
|
|
|
if ((bufsize > c->v.v2.locmaxwin || (ssh_is_simple(ssh) && bufsize>0))
|
|
|
|
&& !c->throttling_conn) {
|
2007-10-03 20:29:27 +00:00
|
|
|
c->throttling_conn = 1;
|
|
|
|
ssh_throttle_conn(ssh, +1);
|
|
|
|
}
|
2004-11-27 15:32:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-17 14:05:41 +00:00
|
|
|
static void ssh_check_termination(Ssh ssh)
|
|
|
|
{
|
|
|
|
if (ssh->version == 2 &&
|
|
|
|
!conf_get_int(ssh->conf, CONF_ssh_no_shell) &&
|
2015-06-22 18:37:27 +00:00
|
|
|
(ssh->channels && count234(ssh->channels) == 0) &&
|
2013-11-17 14:05:41 +00:00
|
|
|
!(ssh->connshare && share_ndownstreams(ssh->connshare) > 0)) {
|
|
|
|
/*
|
|
|
|
* We used to send SSH_MSG_DISCONNECT here, because I'd
|
|
|
|
* believed that _every_ conforming SSH-2 connection had to
|
|
|
|
* end with a disconnect being sent by at least one side;
|
|
|
|
* apparently I was wrong and it's perfectly OK to
|
|
|
|
* unceremoniously slam the connection shut when you're done,
|
|
|
|
* and indeed OpenSSH feels this is more polite than sending a
|
|
|
|
* DISCONNECT. So now we don't.
|
|
|
|
*/
|
|
|
|
ssh_disconnect(ssh, "All channels closed", NULL, 0, TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-28 13:50:02 +00:00
|
|
|
/*
|
|
|
|
* Close any local socket and free any local resources associated with
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
* a channel. This converts the channel into a zombie.
|
2016-05-28 13:50:02 +00:00
|
|
|
*/
|
|
|
|
static void ssh_channel_close_local(struct ssh_channel *c, char const *reason)
|
2004-11-27 15:32:45 +00:00
|
|
|
{
|
2011-09-13 11:44:03 +00:00
|
|
|
Ssh ssh = c->ssh;
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
const char *msg = NULL;
|
|
|
|
|
|
|
|
if (c->sharectx)
|
|
|
|
return;
|
|
|
|
|
|
|
|
msg = chan_log_close_msg(c->chan);
|
|
|
|
chan_free(c->chan);
|
|
|
|
c->chan = zombiechan_new();
|
|
|
|
|
2016-05-28 13:50:02 +00:00
|
|
|
if (msg != NULL) {
|
|
|
|
if (reason != NULL)
|
|
|
|
logeventf(ssh, "%s %s", msg, reason);
|
|
|
|
else
|
|
|
|
logevent(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ssh_channel_destroy(struct ssh_channel *c)
|
|
|
|
{
|
|
|
|
Ssh ssh = c->ssh;
|
|
|
|
|
|
|
|
ssh_channel_close_local(c, NULL);
|
2011-09-13 11:44:03 +00:00
|
|
|
|
|
|
|
del234(ssh->channels, c);
|
2012-08-25 21:06:48 +00:00
|
|
|
if (ssh->version == 2) {
|
2011-09-13 11:44:03 +00:00
|
|
|
bufchain_clear(&c->v.v2.outbuffer);
|
2012-08-25 21:06:48 +00:00
|
|
|
assert(c->v.v2.chanreq_head == NULL);
|
|
|
|
}
|
2011-09-13 11:44:03 +00:00
|
|
|
sfree(c);
|
|
|
|
|
|
|
|
/*
|
2013-11-17 14:05:41 +00:00
|
|
|
* If that was the last channel left open, we might need to
|
|
|
|
* terminate.
|
2011-09-13 11:44:03 +00:00
|
|
|
*/
|
2013-11-17 14:05:41 +00:00
|
|
|
ssh_check_termination(ssh);
|
2011-09-13 11:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ssh2_channel_check_close(struct ssh_channel *c)
|
|
|
|
{
|
|
|
|
Ssh ssh = c->ssh;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
2011-09-13 11:44:03 +00:00
|
|
|
|
2016-05-22 12:50:34 +00:00
|
|
|
assert(ssh->version == 2);
|
2013-09-08 13:20:49 +00:00
|
|
|
if (c->halfopen) {
|
|
|
|
/*
|
|
|
|
* If we've sent out our own CHANNEL_OPEN but not yet seen
|
|
|
|
* either OPEN_CONFIRMATION or OPEN_FAILURE in response, then
|
|
|
|
* it's too early to be sending close messages of any kind.
|
|
|
|
*/
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-08-27 17:37:44 +00:00
|
|
|
if ((!((CLOSES_SENT_EOF | CLOSES_RCVD_EOF) & ~c->closes) ||
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
chan_want_close(c->chan, (c->closes & CLOSES_SENT_EOF),
|
|
|
|
(c->closes & CLOSES_RCVD_EOF))) &&
|
2012-08-27 17:37:44 +00:00
|
|
|
!c->v.v2.chanreq_head &&
|
|
|
|
!(c->closes & CLOSES_SENT_CLOSE)) {
|
2011-09-13 11:44:03 +00:00
|
|
|
/*
|
2012-08-27 17:37:44 +00:00
|
|
|
* We have both sent and received EOF (or the channel is a
|
|
|
|
* zombie), and we have no outstanding channel requests, which
|
|
|
|
* means the channel is in final wind-up. But we haven't sent
|
|
|
|
* CLOSE, so let's do so now.
|
2011-09-13 11:44:03 +00:00
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_CHANNEL_CLOSE);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(pktout, c->remoteid);
|
2011-09-13 11:44:03 +00:00
|
|
|
ssh2_pkt_send(ssh, pktout);
|
2012-08-27 17:37:44 +00:00
|
|
|
c->closes |= CLOSES_SENT_EOF | CLOSES_SENT_CLOSE;
|
2011-09-13 11:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes)) {
|
2012-08-25 21:06:48 +00:00
|
|
|
assert(c->v.v2.chanreq_head == NULL);
|
2011-09-13 11:44:03 +00:00
|
|
|
/*
|
|
|
|
* We have both sent and received CLOSE, which means we're
|
|
|
|
* completely done with the channel.
|
|
|
|
*/
|
|
|
|
ssh_channel_destroy(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-25 22:16:09 +00:00
|
|
|
static void ssh_channel_got_eof(struct ssh_channel *c)
|
2011-09-13 11:44:03 +00:00
|
|
|
{
|
|
|
|
if (c->closes & CLOSES_RCVD_EOF)
|
|
|
|
return; /* already seen EOF */
|
|
|
|
c->closes |= CLOSES_RCVD_EOF;
|
2004-11-27 15:32:45 +00:00
|
|
|
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
chan_send_eof(c->chan);
|
2011-09-13 11:44:03 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_channel_eof(Ssh ssh, PktIn *pktin)
|
2011-09-13 11:44:03 +00:00
|
|
|
{
|
|
|
|
struct ssh_channel *c;
|
|
|
|
|
2016-05-22 21:14:00 +00:00
|
|
|
c = ssh_channel_msg(ssh, pktin);
|
2011-09-13 11:44:03 +00:00
|
|
|
if (!c)
|
|
|
|
return;
|
2016-05-25 22:16:09 +00:00
|
|
|
ssh_channel_got_eof(c);
|
2016-05-25 22:06:20 +00:00
|
|
|
ssh2_channel_check_close(c);
|
2004-11-27 15:32:45 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_channel_close(Ssh ssh, PktIn *pktin)
|
2004-11-27 15:32:45 +00:00
|
|
|
{
|
|
|
|
struct ssh_channel *c;
|
|
|
|
|
2016-05-22 21:14:00 +00:00
|
|
|
c = ssh_channel_msg(ssh, pktin);
|
2007-09-30 19:42:31 +00:00
|
|
|
if (!c)
|
2004-11-27 15:32:45 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
2011-09-13 11:44:03 +00:00
|
|
|
* When we receive CLOSE on a channel, we assume it comes with an
|
|
|
|
* implied EOF if we haven't seen EOF yet.
|
2004-11-27 15:32:45 +00:00
|
|
|
*/
|
2016-05-25 22:16:09 +00:00
|
|
|
ssh_channel_got_eof(c);
|
2011-09-13 11:44:03 +00:00
|
|
|
|
2014-07-06 14:05:39 +00:00
|
|
|
if (!(ssh->remote_bugs & BUG_SENDS_LATE_REQUEST_REPLY)) {
|
|
|
|
/*
|
|
|
|
* It also means we stop expecting to see replies to any
|
|
|
|
* outstanding channel requests, so clean those up too.
|
|
|
|
* (ssh_chanreq_init will enforce by assertion that we don't
|
|
|
|
* subsequently put anything back on this list.)
|
|
|
|
*/
|
|
|
|
while (c->v.v2.chanreq_head) {
|
|
|
|
struct outstanding_channel_request *ocr = c->v.v2.chanreq_head;
|
|
|
|
ocr->handler(c, NULL, ocr->ctx);
|
|
|
|
c->v.v2.chanreq_head = ocr->next;
|
|
|
|
sfree(ocr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-14 09:09:35 +00:00
|
|
|
/*
|
|
|
|
* And we also send an outgoing EOF, if we haven't already, on the
|
|
|
|
* assumption that CLOSE is a pretty forceful announcement that
|
|
|
|
* the remote side is doing away with the entire channel. (If it
|
|
|
|
* had wanted to send us EOF and continue receiving data from us,
|
|
|
|
* it would have just sent CHANNEL_EOF.)
|
|
|
|
*/
|
2011-09-14 09:49:00 +00:00
|
|
|
if (!(c->closes & CLOSES_SENT_EOF)) {
|
2013-07-21 10:12:58 +00:00
|
|
|
/*
|
|
|
|
* Abandon any buffered data we still wanted to send to this
|
|
|
|
* channel. Receiving a CHANNEL_CLOSE is an indication that
|
|
|
|
* the server really wants to get on and _destroy_ this
|
|
|
|
* channel, and it isn't going to send us any further
|
|
|
|
* WINDOW_ADJUSTs to permit us to send pending stuff.
|
|
|
|
*/
|
|
|
|
bufchain_clear(&c->v.v2.outbuffer);
|
|
|
|
|
2011-09-14 09:49:00 +00:00
|
|
|
/*
|
|
|
|
* Send outgoing EOF.
|
|
|
|
*/
|
2018-09-14 12:47:13 +00:00
|
|
|
sshfwd_write_eof(&c->sc);
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure we don't read any more from whatever our local
|
|
|
|
* data source is for this channel. (This will pick up on the
|
|
|
|
* changes made by sshfwd_write_eof.)
|
|
|
|
*/
|
|
|
|
ssh_channel_check_throttle(c);
|
2011-09-14 09:09:35 +00:00
|
|
|
}
|
|
|
|
|
2011-09-13 11:44:03 +00:00
|
|
|
/*
|
|
|
|
* Now process the actual close.
|
|
|
|
*/
|
|
|
|
if (!(c->closes & CLOSES_RCVD_CLOSE)) {
|
|
|
|
c->closes |= CLOSES_RCVD_CLOSE;
|
|
|
|
ssh2_channel_check_close(c);
|
2004-11-27 15:32:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_channel_open_confirmation(Ssh ssh, PktIn *pktin)
|
2004-11-27 15:32:45 +00:00
|
|
|
{
|
|
|
|
struct ssh_channel *c;
|
|
|
|
|
2016-05-22 21:14:00 +00:00
|
|
|
c = ssh_channel_msg(ssh, pktin);
|
2004-11-27 15:32:45 +00:00
|
|
|
if (!c)
|
2007-09-30 19:42:31 +00:00
|
|
|
return;
|
2016-05-22 21:14:00 +00:00
|
|
|
assert(c->halfopen); /* ssh_channel_msg will have enforced this */
|
2018-05-27 17:13:53 +00:00
|
|
|
c->remoteid = get_uint32(pktin);
|
2005-01-22 16:06:21 +00:00
|
|
|
c->halfopen = FALSE;
|
2018-05-27 17:13:53 +00:00
|
|
|
c->v.v2.remwindow = get_uint32(pktin);
|
|
|
|
c->v.v2.remmaxpkt = get_uint32(pktin);
|
2013-09-08 13:20:49 +00:00
|
|
|
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
chan_open_confirmation(c->chan);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now that the channel is fully open, it's possible in principle
|
|
|
|
* to immediately close it. Check whether it wants us to!
|
|
|
|
*
|
|
|
|
* This can occur if a local socket error occurred between us
|
|
|
|
* sending out CHANNEL_OPEN and receiving OPEN_CONFIRMATION. If
|
|
|
|
* that happens, all we can do is immediately initiate close
|
|
|
|
* proceedings now that we know the server's id to put in the
|
|
|
|
* close message. We'll have handled that in this code by having
|
|
|
|
* already turned c->chan into a zombie, so its want_close method
|
|
|
|
* (which ssh2_channel_check_close will consult) will already be
|
|
|
|
* returning TRUE.
|
|
|
|
*/
|
|
|
|
ssh2_channel_check_close(c);
|
2013-09-08 13:20:49 +00:00
|
|
|
|
2011-09-13 11:44:03 +00:00
|
|
|
if (c->pending_eof)
|
2013-09-08 13:20:49 +00:00
|
|
|
ssh_channel_try_eof(c); /* in case we had a pending EOF */
|
2004-11-27 15:32:45 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static char *ssh2_channel_open_failure_error_text(PktIn *pktin)
|
2004-11-27 15:32:45 +00:00
|
|
|
{
|
|
|
|
static const char *const reasons[] = {
|
Log the server's diagnostics if main channel open fails.
This has been a FIXME in the code for ages, because back when the main
channel was always a pty session or a program run in a pipe, there
weren't that many circumstances in which the actual CHANNEL_OPEN could
return failure, so it never seemed like a priority to get round to
pulling the error information out of the CHANNEL_OPEN_FAILURE response
message and including it in PuTTY or Plink's local error message.
However, 'plink -nc' is the real reason why this is actually
important; if you tell the SSH server to make a direct-tcpip network
connection as its main channel, then that can fail for all the usual
network-unreliability reasons, and you actually do want to know which
(did you misspell the hostname, or is the target server refusing
connections, or has network connectivity failed?). This actually bit
me today when I had such a network failure, and had to debug it by
pulling that information manually out of a packet log. Time to
eliminate that FIXME.
So I've pulled the error-extracting code out of the previous handler
for OPEN_FAILURE on non-main channels into a separate function, and
arranged to call that function if the main channel open fails too. In
the process I've made a couple of minor tweaks, e.g. if the server
sends back a reason code we haven't heard of, we say _what_ that
reason code was, and also we at least make a token effort to spot if
we see a packet other than OPEN_{CONFIRMATION,FAILURE} reaching the
main loop in response to the main channel-open.
2017-06-15 17:58:01 +00:00
|
|
|
NULL,
|
|
|
|
"Administratively prohibited",
|
|
|
|
"Connect failed",
|
|
|
|
"Unknown channel type",
|
|
|
|
"Resource shortage",
|
2004-11-27 15:32:45 +00:00
|
|
|
};
|
|
|
|
unsigned reason_code;
|
Log the server's diagnostics if main channel open fails.
This has been a FIXME in the code for ages, because back when the main
channel was always a pty session or a program run in a pipe, there
weren't that many circumstances in which the actual CHANNEL_OPEN could
return failure, so it never seemed like a priority to get round to
pulling the error information out of the CHANNEL_OPEN_FAILURE response
message and including it in PuTTY or Plink's local error message.
However, 'plink -nc' is the real reason why this is actually
important; if you tell the SSH server to make a direct-tcpip network
connection as its main channel, then that can fail for all the usual
network-unreliability reasons, and you actually do want to know which
(did you misspell the hostname, or is the target server refusing
connections, or has network connectivity failed?). This actually bit
me today when I had such a network failure, and had to debug it by
pulling that information manually out of a packet log. Time to
eliminate that FIXME.
So I've pulled the error-extracting code out of the previous handler
for OPEN_FAILURE on non-main channels into a separate function, and
arranged to call that function if the main channel open fails too. In
the process I've made a couple of minor tweaks, e.g. if the server
sends back a reason code we haven't heard of, we say _what_ that
reason code was, and also we at least make a token effort to spot if
we see a packet other than OPEN_{CONFIRMATION,FAILURE} reaching the
main loop in response to the main channel-open.
2017-06-15 17:58:01 +00:00
|
|
|
const char *reason_code_string;
|
|
|
|
char reason_code_buf[256];
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen reason;
|
Log the server's diagnostics if main channel open fails.
This has been a FIXME in the code for ages, because back when the main
channel was always a pty session or a program run in a pipe, there
weren't that many circumstances in which the actual CHANNEL_OPEN could
return failure, so it never seemed like a priority to get round to
pulling the error information out of the CHANNEL_OPEN_FAILURE response
message and including it in PuTTY or Plink's local error message.
However, 'plink -nc' is the real reason why this is actually
important; if you tell the SSH server to make a direct-tcpip network
connection as its main channel, then that can fail for all the usual
network-unreliability reasons, and you actually do want to know which
(did you misspell the hostname, or is the target server refusing
connections, or has network connectivity failed?). This actually bit
me today when I had such a network failure, and had to debug it by
pulling that information manually out of a packet log. Time to
eliminate that FIXME.
So I've pulled the error-extracting code out of the previous handler
for OPEN_FAILURE on non-main channels into a separate function, and
arranged to call that function if the main channel open fails too. In
the process I've made a couple of minor tweaks, e.g. if the server
sends back a reason code we haven't heard of, we say _what_ that
reason code was, and also we at least make a token effort to spot if
we see a packet other than OPEN_{CONFIRMATION,FAILURE} reaching the
main loop in response to the main channel-open.
2017-06-15 17:58:01 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
reason_code = get_uint32(pktin);
|
Log the server's diagnostics if main channel open fails.
This has been a FIXME in the code for ages, because back when the main
channel was always a pty session or a program run in a pipe, there
weren't that many circumstances in which the actual CHANNEL_OPEN could
return failure, so it never seemed like a priority to get round to
pulling the error information out of the CHANNEL_OPEN_FAILURE response
message and including it in PuTTY or Plink's local error message.
However, 'plink -nc' is the real reason why this is actually
important; if you tell the SSH server to make a direct-tcpip network
connection as its main channel, then that can fail for all the usual
network-unreliability reasons, and you actually do want to know which
(did you misspell the hostname, or is the target server refusing
connections, or has network connectivity failed?). This actually bit
me today when I had such a network failure, and had to debug it by
pulling that information manually out of a packet log. Time to
eliminate that FIXME.
So I've pulled the error-extracting code out of the previous handler
for OPEN_FAILURE on non-main channels into a separate function, and
arranged to call that function if the main channel open fails too. In
the process I've made a couple of minor tweaks, e.g. if the server
sends back a reason code we haven't heard of, we say _what_ that
reason code was, and also we at least make a token effort to spot if
we see a packet other than OPEN_{CONFIRMATION,FAILURE} reaching the
main loop in response to the main channel-open.
2017-06-15 17:58:01 +00:00
|
|
|
if (reason_code < lenof(reasons) && reasons[reason_code]) {
|
|
|
|
reason_code_string = reasons[reason_code];
|
|
|
|
} else {
|
|
|
|
reason_code_string = reason_code_buf;
|
|
|
|
sprintf(reason_code_buf, "unknown reason code %#x", reason_code);
|
|
|
|
}
|
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
reason = get_string(pktin);
|
Log the server's diagnostics if main channel open fails.
This has been a FIXME in the code for ages, because back when the main
channel was always a pty session or a program run in a pipe, there
weren't that many circumstances in which the actual CHANNEL_OPEN could
return failure, so it never seemed like a priority to get round to
pulling the error information out of the CHANNEL_OPEN_FAILURE response
message and including it in PuTTY or Plink's local error message.
However, 'plink -nc' is the real reason why this is actually
important; if you tell the SSH server to make a direct-tcpip network
connection as its main channel, then that can fail for all the usual
network-unreliability reasons, and you actually do want to know which
(did you misspell the hostname, or is the target server refusing
connections, or has network connectivity failed?). This actually bit
me today when I had such a network failure, and had to debug it by
pulling that information manually out of a packet log. Time to
eliminate that FIXME.
So I've pulled the error-extracting code out of the previous handler
for OPEN_FAILURE on non-main channels into a separate function, and
arranged to call that function if the main channel open fails too. In
the process I've made a couple of minor tweaks, e.g. if the server
sends back a reason code we haven't heard of, we say _what_ that
reason code was, and also we at least make a token effort to spot if
we see a packet other than OPEN_{CONFIRMATION,FAILURE} reaching the
main loop in response to the main channel-open.
2017-06-15 17:58:01 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
return dupprintf("%s [%.*s]", reason_code_string, PTRLEN_PRINTF(reason));
|
Log the server's diagnostics if main channel open fails.
This has been a FIXME in the code for ages, because back when the main
channel was always a pty session or a program run in a pipe, there
weren't that many circumstances in which the actual CHANNEL_OPEN could
return failure, so it never seemed like a priority to get round to
pulling the error information out of the CHANNEL_OPEN_FAILURE response
message and including it in PuTTY or Plink's local error message.
However, 'plink -nc' is the real reason why this is actually
important; if you tell the SSH server to make a direct-tcpip network
connection as its main channel, then that can fail for all the usual
network-unreliability reasons, and you actually do want to know which
(did you misspell the hostname, or is the target server refusing
connections, or has network connectivity failed?). This actually bit
me today when I had such a network failure, and had to debug it by
pulling that information manually out of a packet log. Time to
eliminate that FIXME.
So I've pulled the error-extracting code out of the previous handler
for OPEN_FAILURE on non-main channels into a separate function, and
arranged to call that function if the main channel open fails too. In
the process I've made a couple of minor tweaks, e.g. if the server
sends back a reason code we haven't heard of, we say _what_ that
reason code was, and also we at least make a token effort to spot if
we see a packet other than OPEN_{CONFIRMATION,FAILURE} reaching the
main loop in response to the main channel-open.
2017-06-15 17:58:01 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_channel_open_failure(Ssh ssh, PktIn *pktin)
|
Log the server's diagnostics if main channel open fails.
This has been a FIXME in the code for ages, because back when the main
channel was always a pty session or a program run in a pipe, there
weren't that many circumstances in which the actual CHANNEL_OPEN could
return failure, so it never seemed like a priority to get round to
pulling the error information out of the CHANNEL_OPEN_FAILURE response
message and including it in PuTTY or Plink's local error message.
However, 'plink -nc' is the real reason why this is actually
important; if you tell the SSH server to make a direct-tcpip network
connection as its main channel, then that can fail for all the usual
network-unreliability reasons, and you actually do want to know which
(did you misspell the hostname, or is the target server refusing
connections, or has network connectivity failed?). This actually bit
me today when I had such a network failure, and had to debug it by
pulling that information manually out of a packet log. Time to
eliminate that FIXME.
So I've pulled the error-extracting code out of the previous handler
for OPEN_FAILURE on non-main channels into a separate function, and
arranged to call that function if the main channel open fails too. In
the process I've made a couple of minor tweaks, e.g. if the server
sends back a reason code we haven't heard of, we say _what_ that
reason code was, and also we at least make a token effort to spot if
we see a packet other than OPEN_{CONFIRMATION,FAILURE} reaching the
main loop in response to the main channel-open.
2017-06-15 17:58:01 +00:00
|
|
|
{
|
2004-11-27 15:32:45 +00:00
|
|
|
struct ssh_channel *c;
|
2013-09-08 13:20:49 +00:00
|
|
|
|
2016-05-22 21:14:00 +00:00
|
|
|
c = ssh_channel_msg(ssh, pktin);
|
2004-11-27 15:32:45 +00:00
|
|
|
if (!c)
|
2007-09-30 19:42:31 +00:00
|
|
|
return;
|
2016-05-22 21:14:00 +00:00
|
|
|
assert(c->halfopen); /* ssh_channel_msg will have enforced this */
|
2004-11-27 15:32:45 +00:00
|
|
|
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
{
|
Log the server's diagnostics if main channel open fails.
This has been a FIXME in the code for ages, because back when the main
channel was always a pty session or a program run in a pipe, there
weren't that many circumstances in which the actual CHANNEL_OPEN could
return failure, so it never seemed like a priority to get round to
pulling the error information out of the CHANNEL_OPEN_FAILURE response
message and including it in PuTTY or Plink's local error message.
However, 'plink -nc' is the real reason why this is actually
important; if you tell the SSH server to make a direct-tcpip network
connection as its main channel, then that can fail for all the usual
network-unreliability reasons, and you actually do want to know which
(did you misspell the hostname, or is the target server refusing
connections, or has network connectivity failed?). This actually bit
me today when I had such a network failure, and had to debug it by
pulling that information manually out of a packet log. Time to
eliminate that FIXME.
So I've pulled the error-extracting code out of the previous handler
for OPEN_FAILURE on non-main channels into a separate function, and
arranged to call that function if the main channel open fails too. In
the process I've made a couple of minor tweaks, e.g. if the server
sends back a reason code we haven't heard of, we say _what_ that
reason code was, and also we at least make a token effort to spot if
we see a packet other than OPEN_{CONFIRMATION,FAILURE} reaching the
main loop in response to the main channel-open.
2017-06-15 17:58:01 +00:00
|
|
|
char *errtext = ssh2_channel_open_failure_error_text(pktin);
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
chan_open_failed(c->chan, errtext);
|
Log the server's diagnostics if main channel open fails.
This has been a FIXME in the code for ages, because back when the main
channel was always a pty session or a program run in a pipe, there
weren't that many circumstances in which the actual CHANNEL_OPEN could
return failure, so it never seemed like a priority to get round to
pulling the error information out of the CHANNEL_OPEN_FAILURE response
message and including it in PuTTY or Plink's local error message.
However, 'plink -nc' is the real reason why this is actually
important; if you tell the SSH server to make a direct-tcpip network
connection as its main channel, then that can fail for all the usual
network-unreliability reasons, and you actually do want to know which
(did you misspell the hostname, or is the target server refusing
connections, or has network connectivity failed?). This actually bit
me today when I had such a network failure, and had to debug it by
pulling that information manually out of a packet log. Time to
eliminate that FIXME.
So I've pulled the error-extracting code out of the previous handler
for OPEN_FAILURE on non-main channels into a separate function, and
arranged to call that function if the main channel open fails too. In
the process I've made a couple of minor tweaks, e.g. if the server
sends back a reason code we haven't heard of, we say _what_ that
reason code was, and also we at least make a token effort to spot if
we see a packet other than OPEN_{CONFIRMATION,FAILURE} reaching the
main loop in response to the main channel-open.
2017-06-15 17:58:01 +00:00
|
|
|
sfree(errtext);
|
2013-09-08 13:20:49 +00:00
|
|
|
}
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
chan_free(c->chan);
|
2004-11-27 15:32:45 +00:00
|
|
|
|
|
|
|
del234(ssh->channels, c);
|
|
|
|
sfree(c);
|
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_channel_request(Ssh ssh, PktIn *pktin)
|
2004-11-27 15:32:45 +00:00
|
|
|
{
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen type;
|
|
|
|
int want_reply;
|
2004-11-27 15:32:45 +00:00
|
|
|
int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */
|
|
|
|
struct ssh_channel *c;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
2004-11-27 15:32:45 +00:00
|
|
|
|
2016-05-22 21:14:00 +00:00
|
|
|
c = ssh_channel_msg(ssh, pktin);
|
2007-09-30 19:42:31 +00:00
|
|
|
if (!c)
|
|
|
|
return;
|
2018-05-27 17:13:53 +00:00
|
|
|
type = get_string(pktin);
|
|
|
|
want_reply = get_bool(pktin);
|
2004-11-27 15:32:45 +00:00
|
|
|
|
2014-07-06 14:05:39 +00:00
|
|
|
if (c->closes & CLOSES_SENT_CLOSE) {
|
|
|
|
/*
|
|
|
|
* We don't reply to channel requests after we've sent
|
|
|
|
* CHANNEL_CLOSE for the channel, because our reply might
|
|
|
|
* cross in the network with the other side's CHANNEL_CLOSE
|
|
|
|
* and arrive after they have wound the channel up completely.
|
|
|
|
*/
|
|
|
|
want_reply = FALSE;
|
|
|
|
}
|
|
|
|
|
2004-11-27 15:32:45 +00:00
|
|
|
/*
|
|
|
|
* Having got the channel number, we now look at
|
|
|
|
* the request type string to see if it's something
|
|
|
|
* we recognise.
|
|
|
|
*/
|
|
|
|
if (c == ssh->mainchan) {
|
|
|
|
/*
|
|
|
|
* We recognise "exit-status" and "exit-signal" on
|
|
|
|
* the primary channel.
|
|
|
|
*/
|
2018-05-27 17:13:53 +00:00
|
|
|
if (ptrlen_eq_string(type, "exit-status")) {
|
2004-11-27 15:32:45 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
ssh->exitcode = get_uint32(pktin);
|
2004-11-27 15:32:45 +00:00
|
|
|
logeventf(ssh, "Server sent command exit status %d",
|
|
|
|
ssh->exitcode);
|
|
|
|
reply = SSH2_MSG_CHANNEL_SUCCESS;
|
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
} else if (ptrlen_eq_string(type, "exit-signal")) {
|
2015-05-15 10:15:42 +00:00
|
|
|
char *fmt_sig = NULL, *fmt_msg = NULL;
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen errmsg;
|
|
|
|
int core = FALSE;
|
|
|
|
int format, exitcode;
|
|
|
|
|
|
|
|
/* ICK: older versions of OpenSSH (e.g. 3.4p1)
|
2004-11-27 15:32:45 +00:00
|
|
|
* provide an `int' for the signal, despite its
|
2007-10-03 21:21:18 +00:00
|
|
|
* having been a `string' in the drafts of RFC 4254 since at
|
2004-11-27 15:32:45 +00:00
|
|
|
* least 2001. (Fixed in session.c 1.147.) Try to
|
|
|
|
* infer which we can safely parse it as. */
|
2018-05-27 17:13:53 +00:00
|
|
|
|
|
|
|
size_t startpos = BinarySource_UPCAST(pktin)->pos;
|
|
|
|
|
|
|
|
for (format = 0; format < 2; format++) {
|
|
|
|
BinarySource_UPCAST(pktin)->pos = startpos;
|
|
|
|
BinarySource_UPCAST(pktin)->err = BSE_NO_ERROR;
|
|
|
|
|
|
|
|
if (format == 0) { /* standard string-based format */
|
|
|
|
ptrlen signame = get_string(pktin);
|
|
|
|
fmt_sig = dupprintf(" \"%.*s\"", PTRLEN_PRINTF(signame));
|
2007-01-17 18:38:45 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Really hideous method of translating the
|
|
|
|
* signal description back into a locally
|
|
|
|
* meaningful number.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (0)
|
|
|
|
;
|
2007-02-17 22:33:11 +00:00
|
|
|
#define TRANSLATE_SIGNAL(s) \
|
2018-05-27 17:13:53 +00:00
|
|
|
else if (ptrlen_eq_string(signame, #s)) \
|
|
|
|
exitcode = 128 + SIG ## s
|
2007-01-17 18:38:45 +00:00
|
|
|
#ifdef SIGABRT
|
2007-02-17 22:33:11 +00:00
|
|
|
TRANSLATE_SIGNAL(ABRT);
|
2007-01-17 18:38:45 +00:00
|
|
|
#endif
|
|
|
|
#ifdef SIGALRM
|
2007-02-17 22:33:11 +00:00
|
|
|
TRANSLATE_SIGNAL(ALRM);
|
2007-01-17 18:38:45 +00:00
|
|
|
#endif
|
|
|
|
#ifdef SIGFPE
|
2007-02-17 22:33:11 +00:00
|
|
|
TRANSLATE_SIGNAL(FPE);
|
2007-01-17 18:38:45 +00:00
|
|
|
#endif
|
|
|
|
#ifdef SIGHUP
|
2007-02-17 22:33:11 +00:00
|
|
|
TRANSLATE_SIGNAL(HUP);
|
2007-01-17 18:38:45 +00:00
|
|
|
#endif
|
|
|
|
#ifdef SIGILL
|
2007-02-17 22:33:11 +00:00
|
|
|
TRANSLATE_SIGNAL(ILL);
|
2007-01-17 18:38:45 +00:00
|
|
|
#endif
|
|
|
|
#ifdef SIGINT
|
2007-02-17 22:33:11 +00:00
|
|
|
TRANSLATE_SIGNAL(INT);
|
2007-01-17 18:38:45 +00:00
|
|
|
#endif
|
|
|
|
#ifdef SIGKILL
|
2007-02-17 22:33:11 +00:00
|
|
|
TRANSLATE_SIGNAL(KILL);
|
2007-01-17 18:38:45 +00:00
|
|
|
#endif
|
|
|
|
#ifdef SIGPIPE
|
2007-02-17 22:33:11 +00:00
|
|
|
TRANSLATE_SIGNAL(PIPE);
|
2007-01-17 18:38:45 +00:00
|
|
|
#endif
|
|
|
|
#ifdef SIGQUIT
|
2007-02-17 22:33:11 +00:00
|
|
|
TRANSLATE_SIGNAL(QUIT);
|
2007-01-17 18:38:45 +00:00
|
|
|
#endif
|
|
|
|
#ifdef SIGSEGV
|
2007-02-17 22:33:11 +00:00
|
|
|
TRANSLATE_SIGNAL(SEGV);
|
2007-01-17 18:38:45 +00:00
|
|
|
#endif
|
|
|
|
#ifdef SIGTERM
|
2007-02-17 22:33:11 +00:00
|
|
|
TRANSLATE_SIGNAL(TERM);
|
2007-01-17 18:38:45 +00:00
|
|
|
#endif
|
|
|
|
#ifdef SIGUSR1
|
2007-02-17 22:33:11 +00:00
|
|
|
TRANSLATE_SIGNAL(USR1);
|
2007-01-17 18:38:45 +00:00
|
|
|
#endif
|
|
|
|
#ifdef SIGUSR2
|
2007-02-17 22:33:11 +00:00
|
|
|
TRANSLATE_SIGNAL(USR2);
|
2007-01-17 18:38:45 +00:00
|
|
|
#endif
|
2007-02-17 22:33:11 +00:00
|
|
|
#undef TRANSLATE_SIGNAL
|
2007-01-17 18:38:45 +00:00
|
|
|
else
|
2018-05-27 17:13:53 +00:00
|
|
|
exitcode = 128;
|
|
|
|
} else { /* nonstandard integer format */
|
|
|
|
unsigned signum = get_uint32(pktin);
|
|
|
|
fmt_sig = dupprintf(" %u", signum);
|
|
|
|
exitcode = 128 + signum;
|
|
|
|
}
|
|
|
|
|
|
|
|
core = get_bool(pktin);
|
|
|
|
errmsg = get_string(pktin); /* error message */
|
|
|
|
get_string(pktin); /* language tag */
|
|
|
|
if (!get_err(pktin) && get_avail(pktin) == 0)
|
|
|
|
break; /* successful parse */
|
|
|
|
|
|
|
|
sfree(fmt_sig);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (format == 2) {
|
|
|
|
fmt_sig = NULL;
|
|
|
|
exitcode = 128;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssh->exitcode = exitcode;
|
|
|
|
if (errmsg.len) {
|
|
|
|
fmt_msg = dupprintf(" (\"%.*s\")", PTRLEN_PRINTF(errmsg));
|
|
|
|
}
|
|
|
|
|
2004-11-27 15:32:45 +00:00
|
|
|
logeventf(ssh, "Server exited on signal%s%s%s",
|
2015-05-15 10:15:42 +00:00
|
|
|
fmt_sig ? fmt_sig : "",
|
|
|
|
core ? " (core dumped)" : "",
|
|
|
|
fmt_msg ? fmt_msg : "");
|
|
|
|
sfree(fmt_sig);
|
|
|
|
sfree(fmt_msg);
|
2004-11-27 15:32:45 +00:00
|
|
|
reply = SSH2_MSG_CHANNEL_SUCCESS;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* This is a channel request we don't know
|
|
|
|
* about, so we now either ignore the request
|
|
|
|
* or respond with CHANNEL_FAILURE, depending
|
|
|
|
* on want_reply.
|
|
|
|
*/
|
|
|
|
reply = SSH2_MSG_CHANNEL_FAILURE;
|
|
|
|
}
|
|
|
|
if (want_reply) {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
pktout = ssh_bpp_new_pktout(ssh->bpp, reply);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(pktout, c->remoteid);
|
2004-11-27 15:32:45 +00:00
|
|
|
ssh2_pkt_send(ssh, pktout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_global_request(Ssh ssh, PktIn *pktin)
|
2004-11-27 15:32:45 +00:00
|
|
|
{
|
2018-05-27 17:13:53 +00:00
|
|
|
int want_reply;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
2004-11-27 15:32:45 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
get_string(pktin); /* ignore request type (see below) */
|
|
|
|
want_reply = get_bool(pktin);
|
2004-11-27 15:32:45 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We currently don't support any global requests
|
|
|
|
* at all, so we either ignore the request or
|
|
|
|
* respond with REQUEST_FAILURE, depending on
|
|
|
|
* want_reply.
|
|
|
|
*/
|
|
|
|
if (want_reply) {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_REQUEST_FAILURE);
|
2004-11-27 15:32:45 +00:00
|
|
|
ssh2_pkt_send(ssh, pktout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-17 11:14:00 +00:00
|
|
|
static struct X11FakeAuth *(ssh_add_sharing_x11_display)(
|
|
|
|
ConnectionLayer *cl, int authtype, ssh_sharing_connstate *share_cs,
|
2018-09-13 08:09:10 +00:00
|
|
|
share_channel *share_chan)
|
2013-11-17 14:05:41 +00:00
|
|
|
{
|
2018-09-17 11:14:00 +00:00
|
|
|
Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
|
2013-11-17 14:05:41 +00:00
|
|
|
struct X11FakeAuth *auth;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make up a new set of fake X11 auth data, and add it to the tree
|
|
|
|
* of currently valid ones with an indication of the sharing
|
|
|
|
* context that it's relevant to.
|
|
|
|
*/
|
|
|
|
auth = x11_invent_fake_auth(ssh->x11authtree, authtype);
|
|
|
|
auth->share_cs = share_cs;
|
|
|
|
auth->share_chan = share_chan;
|
|
|
|
|
|
|
|
return auth;
|
|
|
|
}
|
|
|
|
|
2018-09-17 11:14:00 +00:00
|
|
|
static void (ssh_remove_sharing_x11_display)(
|
|
|
|
ConnectionLayer *cl, struct X11FakeAuth *auth)
|
2013-11-17 14:05:41 +00:00
|
|
|
{
|
2018-09-17 11:14:00 +00:00
|
|
|
Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
|
2013-11-17 14:05:41 +00:00
|
|
|
del234(ssh->x11authtree, auth);
|
|
|
|
x11_free_fake_auth(auth);
|
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_channel_open(Ssh ssh, PktIn *pktin)
|
2004-11-27 15:32:45 +00:00
|
|
|
{
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen type;
|
2004-11-27 15:32:45 +00:00
|
|
|
int peerport;
|
2015-05-15 10:15:42 +00:00
|
|
|
const char *error = NULL;
|
2004-11-27 15:32:45 +00:00
|
|
|
struct ssh_channel *c;
|
|
|
|
unsigned remid, winsize, pktsize;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
2004-11-27 15:32:45 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
type = get_string(pktin);
|
2004-11-27 15:32:45 +00:00
|
|
|
c = snew(struct ssh_channel);
|
|
|
|
c->ssh = ssh;
|
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
remid = get_uint32(pktin);
|
|
|
|
winsize = get_uint32(pktin);
|
|
|
|
pktsize = get_uint32(pktin);
|
2004-11-27 15:32:45 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
if (ptrlen_eq_string(type, "x11")) {
|
|
|
|
char *addrstr = mkstr(get_string(pktin));
|
|
|
|
peerport = get_uint32(pktin);
|
2004-11-27 15:32:45 +00:00
|
|
|
|
|
|
|
logeventf(ssh, "Received X11 connect request from %s:%d",
|
|
|
|
addrstr, peerport);
|
|
|
|
|
2013-11-17 14:05:41 +00:00
|
|
|
if (!ssh->X11_fwd_enabled && !ssh->connshare)
|
2004-11-27 15:32:45 +00:00
|
|
|
error = "X11 forwarding is not enabled";
|
2013-11-17 14:05:23 +00:00
|
|
|
else {
|
2018-09-14 12:47:13 +00:00
|
|
|
c->chan = x11_new_channel(ssh->x11authtree, &c->sc,
|
|
|
|
addrstr, peerport,
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
ssh->connshare != NULL);
|
2013-11-17 14:05:23 +00:00
|
|
|
logevent("Opened X11 forward channel");
|
2004-11-27 15:32:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sfree(addrstr);
|
2018-05-27 17:13:53 +00:00
|
|
|
} else if (ptrlen_eq_string(type, "forwarded-tcpip")) {
|
2004-11-27 15:32:45 +00:00
|
|
|
struct ssh_rportfwd pf, *realpf;
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen peeraddr;
|
|
|
|
|
|
|
|
pf.shost = mkstr(get_string(pktin));
|
|
|
|
pf.sport = get_uint32(pktin);
|
|
|
|
peeraddr = get_string(pktin);
|
|
|
|
peerport = get_uint32(pktin);
|
2004-11-27 15:32:45 +00:00
|
|
|
realpf = find234(ssh->rportfwds, &pf, NULL);
|
2013-11-17 14:03:21 +00:00
|
|
|
logeventf(ssh, "Received remote port %s:%d open request "
|
2016-02-29 19:38:12 +00:00
|
|
|
"from %.*s:%d", pf.shost, pf.sport,
|
2018-05-27 17:13:53 +00:00
|
|
|
PTRLEN_PRINTF(peeraddr), peerport);
|
2013-11-17 14:03:21 +00:00
|
|
|
sfree(pf.shost);
|
|
|
|
|
2004-11-27 15:32:45 +00:00
|
|
|
if (realpf == NULL) {
|
|
|
|
error = "Remote port is not recognised";
|
|
|
|
} else {
|
2013-11-17 14:05:41 +00:00
|
|
|
char *err;
|
|
|
|
|
|
|
|
if (realpf->share_ctx) {
|
|
|
|
/*
|
|
|
|
* This port forwarding is on behalf of a
|
|
|
|
* connection-sharing downstream, so abandon our own
|
|
|
|
* channel-open procedure and just pass the message on
|
|
|
|
* to sshshare.c.
|
|
|
|
*/
|
|
|
|
share_got_pkt_from_server(realpf->share_ctx, pktin->type,
|
2018-06-06 06:17:09 +00:00
|
|
|
BinarySource_UPCAST(pktin)->data,
|
|
|
|
BinarySource_UPCAST(pktin)->len);
|
2013-11-17 14:05:41 +00:00
|
|
|
sfree(c);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-09-14 16:04:39 +00:00
|
|
|
err = portfwdmgr_connect(
|
|
|
|
ssh->portfwdmgr, &c->chan, realpf->dhost, realpf->dport,
|
|
|
|
&c->sc, realpf->addressfamily);
|
2004-11-27 15:32:45 +00:00
|
|
|
logeventf(ssh, "Attempting to forward remote port to "
|
|
|
|
"%s:%d", realpf->dhost, realpf->dport);
|
Refactor ssh.c's APIs to x11fwd.c and portfwd.c.
The most important change is that, where previously ssh.c held the
Socket pointer for each X11 and port forwarding, and the support
modules would find their internal state structure by calling
sk_get_private_ptr on that Socket, it's now the other way round. ssh.c
now directly holds the internal state structure pointer for each
forwarding, and when the support module needs the Socket it looks it
up in a field of that. This will come in handy when I decouple socket
creation from logical forwarding setup, so that X forwardings can
delay actually opening a connection to an X server until they look at
the authentication data and see which server it has to be.
However, while I'm here, I've also taken the opportunity to clean up a
few other points, notably error message handling, and also the fact
that the same kind of state structure was used for both
connection-type and listening-type port forwardings. Now there are
separate PortForwarding and PortListener structure types, which seems
far more sensible.
[originally from svn r10074]
2013-11-17 14:04:41 +00:00
|
|
|
if (err != NULL) {
|
|
|
|
logeventf(ssh, "Port open failed: %s", err);
|
|
|
|
sfree(err);
|
2004-11-27 15:32:45 +00:00
|
|
|
error = "Port open failed";
|
|
|
|
} else {
|
|
|
|
logevent("Forwarded port opened successfully");
|
|
|
|
}
|
|
|
|
}
|
2018-05-27 17:13:53 +00:00
|
|
|
} else if (ptrlen_eq_string(type, "auth-agent@openssh.com")) {
|
2004-11-27 15:32:45 +00:00
|
|
|
if (!ssh->agentfwd_enabled)
|
|
|
|
error = "Agent forwarding is not enabled";
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
else
|
2018-09-14 12:47:13 +00:00
|
|
|
c->chan = agentf_new(&c->sc);
|
2004-11-27 15:32:45 +00:00
|
|
|
} else {
|
|
|
|
error = "Unsupported channel type requested";
|
|
|
|
}
|
|
|
|
|
|
|
|
c->remoteid = remid;
|
2005-01-22 16:06:21 +00:00
|
|
|
c->halfopen = FALSE;
|
2004-11-27 15:32:45 +00:00
|
|
|
if (error) {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_CHANNEL_OPEN_FAILURE);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(pktout, c->remoteid);
|
|
|
|
put_uint32(pktout, SSH2_OPEN_CONNECT_FAILED);
|
|
|
|
put_stringz(pktout, error);
|
|
|
|
put_stringz(pktout, "en"); /* language tag */
|
2004-11-27 15:32:45 +00:00
|
|
|
ssh2_pkt_send(ssh, pktout);
|
|
|
|
logeventf(ssh, "Rejected channel open: %s", error);
|
|
|
|
sfree(c);
|
|
|
|
} else {
|
2016-05-21 21:29:57 +00:00
|
|
|
ssh_channel_init(c);
|
2004-11-27 15:32:45 +00:00
|
|
|
c->v.v2.remwindow = winsize;
|
|
|
|
c->v.v2.remmaxpkt = pktsize;
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
if (c->chan->initial_fixed_window_size) {
|
2013-11-17 14:05:41 +00:00
|
|
|
c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin =
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
c->chan->initial_fixed_window_size;
|
2013-11-17 14:05:41 +00:00
|
|
|
}
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
pktout = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(pktout, c->remoteid);
|
|
|
|
put_uint32(pktout, c->localid);
|
|
|
|
put_uint32(pktout, c->v.v2.locwindow);
|
|
|
|
put_uint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */
|
2004-11-27 15:32:45 +00:00
|
|
|
ssh2_pkt_send(ssh, pktout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-14 12:47:13 +00:00
|
|
|
static void sshchannel_x11_sharing_handover(
|
|
|
|
SshChannel *sc, ssh_sharing_connstate *share_cs, share_channel *share_chan,
|
|
|
|
const char *peer_addr, int peer_port, int endian,
|
|
|
|
int protomajor, int protominor, const void *initial_data, int initial_len)
|
2013-11-17 14:05:41 +00:00
|
|
|
{
|
2018-09-14 12:47:13 +00:00
|
|
|
struct ssh_channel *c = FROMFIELD(sc, struct ssh_channel, sc);
|
|
|
|
|
2013-11-17 14:05:41 +00:00
|
|
|
/*
|
|
|
|
* This function is called when we've just discovered that an X
|
|
|
|
* forwarding channel on which we'd been handling the initial auth
|
|
|
|
* ourselves turns out to be destined for a connection-sharing
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
* downstream. So we turn the channel into a sharing one, meaning
|
2013-11-17 14:05:41 +00:00
|
|
|
* that we completely stop tracking windows and buffering data and
|
|
|
|
* just pass more or less unmodified SSH messages back and forth.
|
|
|
|
*/
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
c->sharectx = share_cs;
|
2013-11-17 14:05:41 +00:00
|
|
|
share_setup_x11_channel(share_cs, share_chan,
|
|
|
|
c->localid, c->remoteid, c->v.v2.remwindow,
|
|
|
|
c->v.v2.remmaxpkt, c->v.v2.locwindow,
|
|
|
|
peer_addr, peer_port, endian,
|
|
|
|
protomajor, protominor,
|
|
|
|
initial_data, initial_len);
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
chan_free(c->chan);
|
|
|
|
c->chan = NULL;
|
2013-11-17 14:05:41 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 12:47:13 +00:00
|
|
|
static void sshchannel_window_override_removed(SshChannel *sc)
|
2013-11-17 14:05:41 +00:00
|
|
|
{
|
2018-09-14 12:47:13 +00:00
|
|
|
struct ssh_channel *c = FROMFIELD(sc, struct ssh_channel, sc);
|
|
|
|
|
2013-11-17 14:05:41 +00:00
|
|
|
/*
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
* This function is called when a client-side Channel has just
|
|
|
|
* stopped requiring an initial fixed-size window.
|
2013-11-17 14:05:41 +00:00
|
|
|
*/
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
assert(!c->chan->initial_fixed_window_size);
|
2018-06-03 06:11:10 +00:00
|
|
|
if (c->ssh->version == 2)
|
|
|
|
ssh2_set_window(
|
|
|
|
c, ssh_is_simple(c->ssh) ? OUR_V2_BIGWIN : OUR_V2_WINSIZE);
|
2013-11-17 14:05:41 +00:00
|
|
|
}
|
|
|
|
|
2005-05-12 15:09:35 +00:00
|
|
|
/*
|
2010-12-27 01:19:13 +00:00
|
|
|
* Buffer banner messages for later display at some convenient point,
|
|
|
|
* if we're going to display them.
|
2005-05-12 15:09:35 +00:00
|
|
|
*/
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_userauth_banner(Ssh ssh, PktIn *pktin)
|
2005-05-12 15:09:35 +00:00
|
|
|
{
|
|
|
|
/* Arbitrary limit to prevent unbounded inflation of buffer */
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (conf_get_int(ssh->conf, CONF_ssh_show_banner) &&
|
2010-12-27 01:19:13 +00:00
|
|
|
bufchain_size(&ssh->banner) <= 131072) {
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen banner = get_string(pktin);
|
|
|
|
if (banner.len)
|
2018-09-19 17:22:36 +00:00
|
|
|
sanitise_term_data(&ssh->banner, banner.ptr, banner.len);
|
2005-05-12 15:09:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_setup_x11(struct ssh_channel *c, PktIn *pktin,
|
2012-08-30 18:44:35 +00:00
|
|
|
void *ctx)
|
2012-08-25 13:39:32 +00:00
|
|
|
{
|
2012-08-30 18:44:35 +00:00
|
|
|
struct ssh2_setup_x11_state {
|
2012-08-25 13:39:32 +00:00
|
|
|
int crLine;
|
|
|
|
};
|
|
|
|
Ssh ssh = c->ssh;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
2012-08-30 18:44:35 +00:00
|
|
|
crStateP(ssh2_setup_x11_state, ctx);
|
2012-08-25 13:39:32 +00:00
|
|
|
|
|
|
|
crBeginState;
|
|
|
|
|
2012-08-30 18:44:35 +00:00
|
|
|
logevent("Requesting X11 forwarding");
|
|
|
|
pktout = ssh2_chanreq_init(ssh->mainchan, "x11-req",
|
|
|
|
ssh2_setup_x11, s);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_bool(pktout, 0); /* many connections */
|
|
|
|
put_stringz(pktout, ssh->x11auth->protoname);
|
|
|
|
put_stringz(pktout, ssh->x11auth->datastring);
|
|
|
|
put_uint32(pktout, ssh->x11disp->screennum);
|
2012-08-30 18:44:35 +00:00
|
|
|
ssh2_pkt_send(ssh, pktout);
|
2012-08-25 13:39:32 +00:00
|
|
|
|
2013-07-14 17:08:35 +00:00
|
|
|
/* Wait to be called back with either a response packet, or NULL
|
|
|
|
* meaning clean up and free our data */
|
|
|
|
crReturnV;
|
2012-08-25 13:39:32 +00:00
|
|
|
|
2012-08-30 18:44:35 +00:00
|
|
|
if (pktin) {
|
|
|
|
if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) {
|
|
|
|
logevent("X11 forwarding enabled");
|
|
|
|
ssh->X11_fwd_enabled = TRUE;
|
|
|
|
} else
|
|
|
|
logevent("X11 forwarding refused");
|
2012-08-25 13:39:32 +00:00
|
|
|
}
|
2012-08-30 18:44:35 +00:00
|
|
|
|
2012-08-27 18:44:06 +00:00
|
|
|
crFinishFreeV;
|
2012-08-25 13:39:32 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_setup_agent(struct ssh_channel *c, PktIn *pktin,
|
2012-08-25 13:39:32 +00:00
|
|
|
void *ctx)
|
|
|
|
{
|
2012-08-30 18:44:35 +00:00
|
|
|
struct ssh2_setup_agent_state {
|
2012-08-25 13:39:32 +00:00
|
|
|
int crLine;
|
|
|
|
};
|
|
|
|
Ssh ssh = c->ssh;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
2012-08-30 18:44:35 +00:00
|
|
|
crStateP(ssh2_setup_agent_state, ctx);
|
2012-08-25 13:39:32 +00:00
|
|
|
|
|
|
|
crBeginState;
|
|
|
|
|
2012-08-30 18:44:35 +00:00
|
|
|
logevent("Requesting OpenSSH-style agent forwarding");
|
|
|
|
pktout = ssh2_chanreq_init(ssh->mainchan, "auth-agent-req@openssh.com",
|
|
|
|
ssh2_setup_agent, s);
|
|
|
|
ssh2_pkt_send(ssh, pktout);
|
2012-08-25 13:39:32 +00:00
|
|
|
|
2013-07-14 17:08:35 +00:00
|
|
|
/* Wait to be called back with either a response packet, or NULL
|
|
|
|
* meaning clean up and free our data */
|
|
|
|
crReturnV;
|
2012-08-25 13:39:32 +00:00
|
|
|
|
2012-08-30 18:44:35 +00:00
|
|
|
if (pktin) {
|
|
|
|
if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) {
|
|
|
|
logevent("Agent forwarding enabled");
|
|
|
|
ssh->agentfwd_enabled = TRUE;
|
|
|
|
} else
|
|
|
|
logevent("Agent forwarding refused");
|
2012-08-25 13:39:32 +00:00
|
|
|
}
|
2012-08-30 18:44:35 +00:00
|
|
|
|
2012-08-27 18:44:06 +00:00
|
|
|
crFinishFreeV;
|
2012-08-25 13:39:32 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_setup_pty(struct ssh_channel *c, PktIn *pktin,
|
2012-08-25 13:39:32 +00:00
|
|
|
void *ctx)
|
|
|
|
{
|
2012-08-30 18:44:35 +00:00
|
|
|
struct ssh2_setup_pty_state {
|
2012-08-25 13:39:32 +00:00
|
|
|
int crLine;
|
|
|
|
};
|
|
|
|
Ssh ssh = c->ssh;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
2012-08-30 18:44:35 +00:00
|
|
|
crStateP(ssh2_setup_pty_state, ctx);
|
2012-08-25 13:39:32 +00:00
|
|
|
|
|
|
|
crBeginState;
|
|
|
|
|
2012-08-30 18:44:35 +00:00
|
|
|
/* Unpick the terminal-speed string. */
|
|
|
|
/* XXX perhaps we should allow no speeds to be sent. */
|
|
|
|
ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */
|
|
|
|
sscanf(conf_get_str(ssh->conf, CONF_termspeed), "%d,%d", &ssh->ospeed, &ssh->ispeed);
|
|
|
|
/* Build the pty request. */
|
|
|
|
pktout = ssh2_chanreq_init(ssh->mainchan, "pty-req",
|
|
|
|
ssh2_setup_pty, s);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(pktout, conf_get_str(ssh->conf, CONF_termtype));
|
|
|
|
put_uint32(pktout, ssh->term_width);
|
|
|
|
put_uint32(pktout, ssh->term_height);
|
|
|
|
put_uint32(pktout, 0); /* pixel width */
|
|
|
|
put_uint32(pktout, 0); /* pixel height */
|
2018-05-24 09:39:07 +00:00
|
|
|
{
|
|
|
|
strbuf *modebuf = strbuf_new();
|
2018-09-19 19:36:40 +00:00
|
|
|
write_ttymodes_to_packet_from_conf(
|
|
|
|
BinarySink_UPCAST(modebuf), ssh->frontend, ssh->conf,
|
|
|
|
2, ssh->ospeed, ssh->ispeed);
|
2018-05-24 09:39:07 +00:00
|
|
|
put_stringsb(pktout, modebuf);
|
|
|
|
}
|
2012-08-30 18:44:35 +00:00
|
|
|
ssh2_pkt_send(ssh, pktout);
|
|
|
|
ssh->state = SSH_STATE_INTERMED;
|
2012-08-25 13:39:32 +00:00
|
|
|
|
2013-07-14 17:08:35 +00:00
|
|
|
/* Wait to be called back with either a response packet, or NULL
|
|
|
|
* meaning clean up and free our data */
|
|
|
|
crReturnV;
|
2012-08-25 13:39:32 +00:00
|
|
|
|
2012-08-30 18:44:35 +00:00
|
|
|
if (pktin) {
|
|
|
|
if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) {
|
|
|
|
logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)",
|
|
|
|
ssh->ospeed, ssh->ispeed);
|
|
|
|
ssh->got_pty = TRUE;
|
|
|
|
} else {
|
|
|
|
c_write_str(ssh, "Server refused to allocate pty\r\n");
|
|
|
|
ssh->editing = ssh->echoing = 1;
|
|
|
|
}
|
2012-08-25 13:39:32 +00:00
|
|
|
}
|
2012-08-30 18:44:35 +00:00
|
|
|
|
2012-08-27 18:44:06 +00:00
|
|
|
crFinishFreeV;
|
2012-08-25 13:39:32 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_setup_env(struct ssh_channel *c, PktIn *pktin,
|
2012-08-25 13:39:32 +00:00
|
|
|
void *ctx)
|
|
|
|
{
|
|
|
|
struct ssh2_setup_env_state {
|
|
|
|
int crLine;
|
|
|
|
int num_env, env_left, env_ok;
|
|
|
|
};
|
|
|
|
Ssh ssh = c->ssh;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
2012-08-25 13:39:32 +00:00
|
|
|
crStateP(ssh2_setup_env_state, ctx);
|
|
|
|
|
|
|
|
crBeginState;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send environment variables.
|
|
|
|
*
|
|
|
|
* Simplest thing here is to send all the requests at once, and
|
|
|
|
* then wait for a whole bunch of successes or failures.
|
|
|
|
*/
|
|
|
|
s->num_env = 0;
|
2012-08-30 18:44:35 +00:00
|
|
|
{
|
2012-08-25 13:39:32 +00:00
|
|
|
char *key, *val;
|
|
|
|
|
|
|
|
for (val = conf_get_str_strs(ssh->conf, CONF_environmt, NULL, &key);
|
|
|
|
val != NULL;
|
|
|
|
val = conf_get_str_strs(ssh->conf, CONF_environmt, key, &key)) {
|
2012-08-25 15:57:05 +00:00
|
|
|
pktout = ssh2_chanreq_init(ssh->mainchan, "env", ssh2_setup_env, s);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(pktout, key);
|
|
|
|
put_stringz(pktout, val);
|
2012-08-25 13:39:32 +00:00
|
|
|
ssh2_pkt_send(ssh, pktout);
|
|
|
|
|
|
|
|
s->num_env++;
|
|
|
|
}
|
|
|
|
if (s->num_env)
|
|
|
|
logeventf(ssh, "Sent %d environment variables", s->num_env);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->num_env) {
|
|
|
|
s->env_ok = 0;
|
|
|
|
s->env_left = s->num_env;
|
|
|
|
|
|
|
|
while (s->env_left > 0) {
|
2013-07-14 17:08:35 +00:00
|
|
|
/* Wait to be called back with either a response packet,
|
|
|
|
* or NULL meaning clean up and free our data */
|
|
|
|
crReturnV;
|
2012-08-25 21:06:48 +00:00
|
|
|
if (!pktin) goto out;
|
2012-08-25 15:04:29 +00:00
|
|
|
if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS)
|
2012-08-25 13:39:32 +00:00
|
|
|
s->env_ok++;
|
|
|
|
s->env_left--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s->env_ok == s->num_env) {
|
|
|
|
logevent("All environment variables successfully set");
|
|
|
|
} else if (s->env_ok == 0) {
|
|
|
|
logevent("All environment variables refused");
|
|
|
|
c_write_str(ssh, "Server refused to set environment variables\r\n");
|
|
|
|
} else {
|
|
|
|
logeventf(ssh, "%d environment variables refused",
|
|
|
|
s->num_env - s->env_ok);
|
|
|
|
c_write_str(ssh, "Server refused to set all environment variables\r\n");
|
|
|
|
}
|
|
|
|
}
|
2012-08-27 14:34:41 +00:00
|
|
|
out:;
|
2012-08-27 18:44:06 +00:00
|
|
|
crFinishFreeV;
|
2012-08-25 13:39:32 +00:00
|
|
|
}
|
|
|
|
|
2000-09-07 16:33:49 +00:00
|
|
|
/*
|
2018-05-18 06:22:59 +00:00
|
|
|
* Handle the SSH-2 userauth layer.
|
2000-09-07 16:33:49 +00:00
|
|
|
*/
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_userauth(Ssh ssh, PktIn *pktin)
|
2012-08-21 22:04:56 +00:00
|
|
|
{
|
2018-05-18 06:22:58 +00:00
|
|
|
pktin->refcount++; /* avoid packet being freed when we return */
|
2018-05-18 06:22:59 +00:00
|
|
|
pq_push(&ssh->pq_ssh2_userauth, pktin);
|
2018-05-31 17:42:47 +00:00
|
|
|
if (pktin->type == SSH2_MSG_USERAUTH_SUCCESS) {
|
|
|
|
/*
|
|
|
|
* The very instant we see this message, the connection
|
|
|
|
* protocol has officially started, which means we must
|
|
|
|
* install the dispatch-table entries for all the
|
|
|
|
* connection-layer messages. In particular, we must do this
|
|
|
|
* _before_ we return to the loop in ssh_process_pq_full
|
|
|
|
* that's processing the currently queued packets through the
|
|
|
|
* dispatch table, because if (say) an SSH_MSG_GLOBAL_REQUEST
|
|
|
|
* is already pending in pq_full, we can't afford to delay
|
|
|
|
* installing its dispatch table entry until after that queue
|
|
|
|
* run is done.
|
|
|
|
*/
|
|
|
|
ssh2_connection_setup(ssh);
|
|
|
|
}
|
2018-05-18 06:22:59 +00:00
|
|
|
queue_idempotent_callback(&ssh->ssh2_userauth_icb);
|
2012-08-25 13:39:32 +00:00
|
|
|
}
|
|
|
|
|
2018-05-18 06:22:59 +00:00
|
|
|
static void do_ssh2_userauth(void *vctx)
|
2000-09-07 16:33:49 +00:00
|
|
|
{
|
2018-05-18 06:22:58 +00:00
|
|
|
Ssh ssh = (Ssh)vctx;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktIn *pktin;
|
2018-05-18 06:22:58 +00:00
|
|
|
|
2018-05-18 06:22:59 +00:00
|
|
|
struct do_ssh2_userauth_state {
|
2012-08-18 09:30:01 +00:00
|
|
|
int crLine;
|
2002-10-25 11:30:33 +00:00
|
|
|
enum {
|
|
|
|
AUTH_TYPE_NONE,
|
|
|
|
AUTH_TYPE_PUBLICKEY,
|
|
|
|
AUTH_TYPE_PUBLICKEY_OFFER_LOUD,
|
|
|
|
AUTH_TYPE_PUBLICKEY_OFFER_QUIET,
|
|
|
|
AUTH_TYPE_PASSWORD,
|
2011-07-16 12:06:32 +00:00
|
|
|
AUTH_TYPE_GSSAPI, /* always QUIET */
|
2002-10-25 11:30:33 +00:00
|
|
|
AUTH_TYPE_KEYBOARD_INTERACTIVE,
|
|
|
|
AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET
|
|
|
|
} type;
|
2005-05-05 22:37:54 +00:00
|
|
|
int done_service_req;
|
2018-05-18 06:22:58 +00:00
|
|
|
int need_pw, can_pubkey, can_passwd, can_keyb_inter;
|
2018-05-18 06:22:58 +00:00
|
|
|
int userpass_ret;
|
2005-11-13 16:06:41 +00:00
|
|
|
int tried_pubkey_config, done_agent;
|
2008-12-02 18:18:32 +00:00
|
|
|
#ifndef NO_GSSAPI
|
2008-08-10 13:10:31 +00:00
|
|
|
int can_gssapi;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
int can_gssapi_keyex_auth;
|
2008-08-10 13:10:31 +00:00
|
|
|
int tried_gssapi;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
int tried_gssapi_keyex_auth;
|
|
|
|
time_t gss_cred_expiry;
|
2008-12-02 18:18:32 +00:00
|
|
|
#endif
|
2005-10-30 20:24:09 +00:00
|
|
|
int kbd_inter_refused;
|
2011-03-04 22:34:47 +00:00
|
|
|
int we_are_in, userauth_success;
|
2005-10-30 20:24:09 +00:00
|
|
|
prompts_t *cur_prompt;
|
|
|
|
int num_prompts;
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
char *username;
|
2005-11-04 14:49:22 +00:00
|
|
|
char *password;
|
2002-10-25 11:30:33 +00:00
|
|
|
int got_username;
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf *publickey_blob;
|
2015-05-12 11:30:25 +00:00
|
|
|
int privatekey_available, privatekey_encrypted;
|
2005-10-30 20:24:09 +00:00
|
|
|
char *publickey_algorithm;
|
|
|
|
char *publickey_comment;
|
2018-05-27 22:58:20 +00:00
|
|
|
unsigned char *agent_response;
|
|
|
|
BinarySource asrc[1]; /* for reading SSH agent response */
|
|
|
|
size_t pkblob_pos_in_agent;
|
2002-10-25 11:30:33 +00:00
|
|
|
int keyi, nkeys;
|
2018-05-27 22:58:20 +00:00
|
|
|
ptrlen pk, alg, comment;
|
|
|
|
int len;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
Filename *keyfile;
|
2008-12-02 18:18:32 +00:00
|
|
|
#ifndef NO_GSSAPI
|
2008-08-10 13:10:31 +00:00
|
|
|
Ssh_gss_ctx gss_ctx;
|
|
|
|
Ssh_gss_buf gss_buf;
|
|
|
|
Ssh_gss_buf gss_rcvtok, gss_sndtok;
|
|
|
|
Ssh_gss_stat gss_stat;
|
2008-12-02 18:18:32 +00:00
|
|
|
#endif
|
2002-10-25 11:30:33 +00:00
|
|
|
};
|
2018-05-18 06:22:59 +00:00
|
|
|
crState(do_ssh2_userauth_state);
|
2002-10-25 11:30:33 +00:00
|
|
|
|
2012-08-18 09:30:01 +00:00
|
|
|
crBeginState;
|
2000-09-05 14:28:17 +00:00
|
|
|
|
2012-08-21 22:04:56 +00:00
|
|
|
/* Register as a handler for all the messages this coroutine handles. */
|
2018-05-18 06:22:59 +00:00
|
|
|
ssh->packet_dispatch[SSH2_MSG_SERVICE_ACCEPT] = ssh2_msg_userauth;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_USERAUTH_REQUEST] = ssh2_msg_userauth;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_USERAUTH_FAILURE] = ssh2_msg_userauth;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_USERAUTH_SUCCESS] = ssh2_msg_userauth;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = ssh2_msg_userauth;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_USERAUTH_PK_OK] = ssh2_msg_userauth;
|
|
|
|
/* ssh->packet_dispatch[SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ] = ssh2_msg_userauth; duplicate case value */
|
|
|
|
/* ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_REQUEST] = ssh2_msg_userauth; duplicate case value */
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_RESPONSE] = ssh2_msg_userauth;
|
2012-08-21 22:04:56 +00:00
|
|
|
|
2005-05-05 22:37:54 +00:00
|
|
|
s->done_service_req = FALSE;
|
2011-03-04 22:34:47 +00:00
|
|
|
s->we_are_in = s->userauth_success = FALSE;
|
2013-11-17 14:05:41 +00:00
|
|
|
s->agent_response = NULL;
|
2008-12-02 18:18:32 +00:00
|
|
|
#ifndef NO_GSSAPI
|
2008-08-10 13:10:31 +00:00
|
|
|
s->tried_gssapi = FALSE;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
s->tried_gssapi_keyex_auth = FALSE;
|
2008-12-02 18:18:32 +00:00
|
|
|
#endif
|
2008-08-10 13:10:31 +00:00
|
|
|
|
2018-05-18 06:22:59 +00:00
|
|
|
if (!conf_get_int(ssh->conf, CONF_ssh_no_userauth)) {
|
|
|
|
/*
|
|
|
|
* Request userauth protocol, and await a response to it.
|
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_SERVICE_REQUEST);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, "ssh-userauth");
|
2018-05-18 06:22:59 +00:00
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_userauth)) != NULL);
|
|
|
|
if (pktin->type == SSH2_MSG_SERVICE_ACCEPT)
|
|
|
|
s->done_service_req = TRUE;
|
|
|
|
}
|
|
|
|
if (!s->done_service_req) {
|
|
|
|
/*
|
|
|
|
* Request connection protocol directly, without authentication.
|
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_SERVICE_REQUEST);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, "ssh-connection");
|
2018-05-18 06:22:59 +00:00
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_userauth)) != NULL);
|
|
|
|
if (pktin->type == SSH2_MSG_SERVICE_ACCEPT) {
|
|
|
|
s->we_are_in = TRUE; /* no auth required */
|
|
|
|
} else {
|
|
|
|
bombout(("Server refused service request"));
|
|
|
|
crStopV;
|
2013-11-17 14:05:41 +00:00
|
|
|
}
|
2000-09-22 11:04:57 +00:00
|
|
|
}
|
2000-09-07 16:33:49 +00:00
|
|
|
|
2005-11-13 16:06:41 +00:00
|
|
|
/* Arrange to be able to deal with any BANNERs that come in.
|
|
|
|
* (We do this now as packets may come in during the next bit.) */
|
|
|
|
bufchain_init(&ssh->banner);
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] =
|
|
|
|
ssh2_msg_userauth_banner;
|
|
|
|
|
2005-10-30 20:24:09 +00:00
|
|
|
/*
|
|
|
|
* Misc one-time setup for authentication.
|
|
|
|
*/
|
|
|
|
s->publickey_blob = NULL;
|
|
|
|
if (!s->we_are_in) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Load the public half of any configured public key file
|
|
|
|
* for later use.
|
|
|
|
*/
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
|
2011-10-02 11:01:57 +00:00
|
|
|
if (!filename_is_null(s->keyfile)) {
|
2005-10-30 20:24:09 +00:00
|
|
|
int keytype;
|
2015-05-12 11:30:25 +00:00
|
|
|
logeventf(ssh, "Reading key file \"%.150s\"",
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
filename_to_str(s->keyfile));
|
|
|
|
keytype = key_type(s->keyfile);
|
2015-05-12 11:30:25 +00:00
|
|
|
if (keytype == SSH_KEYTYPE_SSH2 ||
|
|
|
|
keytype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 ||
|
|
|
|
keytype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) {
|
2005-10-30 20:24:09 +00:00
|
|
|
const char *error;
|
2018-05-24 09:59:39 +00:00
|
|
|
s->publickey_blob = strbuf_new();
|
|
|
|
if (ssh2_userkey_loadpub(s->keyfile,
|
2005-10-30 20:24:09 +00:00
|
|
|
&s->publickey_algorithm,
|
2018-05-24 09:59:39 +00:00
|
|
|
BinarySink_UPCAST(s->publickey_blob),
|
|
|
|
&s->publickey_comment, &error)) {
|
2015-05-12 11:30:25 +00:00
|
|
|
s->privatekey_available = (keytype == SSH_KEYTYPE_SSH2);
|
|
|
|
if (!s->privatekey_available)
|
|
|
|
logeventf(ssh, "Key file contains public key only");
|
|
|
|
s->privatekey_encrypted =
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
ssh2_userkey_encrypted(s->keyfile, NULL);
|
2005-10-30 20:24:09 +00:00
|
|
|
} else {
|
|
|
|
char *msgbuf;
|
2015-05-12 11:30:25 +00:00
|
|
|
logeventf(ssh, "Unable to load key (%s)",
|
2005-10-30 20:24:09 +00:00
|
|
|
error);
|
2015-05-12 11:30:25 +00:00
|
|
|
msgbuf = dupprintf("Unable to load key file "
|
2005-10-30 20:24:09 +00:00
|
|
|
"\"%.150s\" (%s)\r\n",
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
filename_to_str(s->keyfile),
|
2005-10-30 20:24:09 +00:00
|
|
|
error);
|
|
|
|
c_write_str(ssh, msgbuf);
|
|
|
|
sfree(msgbuf);
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf_free(s->publickey_blob);
|
|
|
|
s->publickey_blob = NULL;
|
2005-10-30 20:24:09 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
char *msgbuf;
|
|
|
|
logeventf(ssh, "Unable to use this key file (%s)",
|
|
|
|
key_type_to_str(keytype));
|
|
|
|
msgbuf = dupprintf("Unable to use key file \"%.150s\""
|
|
|
|
" (%s)\r\n",
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
filename_to_str(s->keyfile),
|
2005-10-30 20:24:09 +00:00
|
|
|
key_type_to_str(keytype));
|
|
|
|
c_write_str(ssh, msgbuf);
|
|
|
|
sfree(msgbuf);
|
|
|
|
s->publickey_blob = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-11-13 16:06:41 +00:00
|
|
|
/*
|
|
|
|
* Find out about any keys Pageant has (but if there's a
|
|
|
|
* public key configured, filter out all others).
|
|
|
|
*/
|
|
|
|
s->nkeys = 0;
|
|
|
|
s->agent_response = NULL;
|
2018-05-27 22:58:20 +00:00
|
|
|
s->pkblob_pos_in_agent = 0;
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (conf_get_int(ssh->conf, CONF_tryagent) && agent_exists()) {
|
2005-11-13 16:06:41 +00:00
|
|
|
void *r;
|
2018-05-27 22:58:20 +00:00
|
|
|
int rlen;
|
2018-05-24 12:18:13 +00:00
|
|
|
strbuf *agent_request;
|
2005-11-13 16:06:41 +00:00
|
|
|
|
|
|
|
logevent("Pageant is running. Requesting keys.");
|
|
|
|
|
|
|
|
/* Request the keys held by the agent. */
|
2018-05-24 12:18:13 +00:00
|
|
|
agent_request = strbuf_new_for_agent_query();
|
|
|
|
put_byte(agent_request, SSH2_AGENTC_REQUEST_IDENTITIES);
|
2017-01-29 20:24:15 +00:00
|
|
|
ssh->auth_agent_query = agent_query(
|
2018-05-27 22:58:20 +00:00
|
|
|
agent_request, &r, &rlen, ssh_agent_callback, ssh);
|
2018-05-24 12:18:13 +00:00
|
|
|
strbuf_free(agent_request);
|
|
|
|
|
2017-01-29 20:24:15 +00:00
|
|
|
if (ssh->auth_agent_query) {
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->agent_response = NULL;
|
2018-05-18 06:22:58 +00:00
|
|
|
crWaitUntilV(ssh->agent_response);
|
2005-11-13 16:06:41 +00:00
|
|
|
r = ssh->agent_response;
|
2018-05-27 22:58:20 +00:00
|
|
|
rlen = ssh->agent_response_len;
|
2005-11-13 16:06:41 +00:00
|
|
|
}
|
2018-05-27 22:58:20 +00:00
|
|
|
s->agent_response = r;
|
|
|
|
BinarySource_BARE_INIT(s->asrc, r, rlen);
|
|
|
|
get_uint32(s->asrc); /* skip length field */
|
|
|
|
if (get_byte(s->asrc) == SSH2_AGENT_IDENTITIES_ANSWER) {
|
2005-11-13 16:06:41 +00:00
|
|
|
int keyi;
|
2018-05-27 22:58:20 +00:00
|
|
|
|
|
|
|
s->nkeys = toint(get_uint32(s->asrc));
|
2013-07-14 10:45:54 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Vet the Pageant response to ensure that the key
|
|
|
|
* count and blob lengths make sense.
|
|
|
|
*/
|
|
|
|
if (s->nkeys < 0) {
|
|
|
|
logeventf(ssh, "Pageant response contained a negative"
|
|
|
|
" key count %d", s->nkeys);
|
|
|
|
s->nkeys = 0;
|
|
|
|
goto done_agent_query;
|
|
|
|
} else {
|
2018-05-27 22:58:20 +00:00
|
|
|
logeventf(ssh, "Pageant has %d SSH-2 keys", s->nkeys);
|
2013-07-14 10:45:54 +00:00
|
|
|
|
2018-05-27 22:58:20 +00:00
|
|
|
/* See if configured key is in agent. */
|
2013-07-14 10:45:54 +00:00
|
|
|
for (keyi = 0; keyi < s->nkeys; keyi++) {
|
2018-05-27 22:58:20 +00:00
|
|
|
size_t pos = s->asrc->pos;
|
|
|
|
ptrlen blob = get_string(s->asrc);
|
|
|
|
get_string(s->asrc); /* skip comment */
|
|
|
|
if (get_err(s->asrc)) {
|
2013-07-14 10:45:54 +00:00
|
|
|
logeventf(ssh, "Pageant response was truncated");
|
|
|
|
s->nkeys = 0;
|
|
|
|
goto done_agent_query;
|
|
|
|
}
|
|
|
|
|
2018-05-27 22:58:20 +00:00
|
|
|
if (s->publickey_blob &&
|
|
|
|
blob.len == s->publickey_blob->len &&
|
|
|
|
!memcmp(blob.ptr, s->publickey_blob->s,
|
2018-05-24 09:59:39 +00:00
|
|
|
s->publickey_blob->len)) {
|
2005-11-13 16:06:41 +00:00
|
|
|
logeventf(ssh, "Pageant key #%d matches "
|
|
|
|
"configured key file", keyi);
|
|
|
|
s->keyi = keyi;
|
2018-05-27 22:58:20 +00:00
|
|
|
s->pkblob_pos_in_agent = pos;
|
2005-11-13 16:06:41 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-05-27 22:58:20 +00:00
|
|
|
if (s->publickey_blob && !s->pkblob_pos_in_agent) {
|
2005-11-13 16:06:41 +00:00
|
|
|
logevent("Configured key file not in Pageant");
|
|
|
|
s->nkeys = 0;
|
|
|
|
}
|
|
|
|
}
|
2011-06-08 20:39:06 +00:00
|
|
|
} else {
|
|
|
|
logevent("Failed to get reply from Pageant");
|
2005-11-13 16:06:41 +00:00
|
|
|
}
|
2013-07-14 10:45:54 +00:00
|
|
|
done_agent_query:;
|
2005-11-13 16:06:41 +00:00
|
|
|
}
|
|
|
|
|
2005-10-30 20:24:09 +00:00
|
|
|
}
|
|
|
|
|
2000-09-07 16:33:49 +00:00
|
|
|
/*
|
2001-03-03 13:53:44 +00:00
|
|
|
* We repeat this whole loop, including the username prompt,
|
|
|
|
* until we manage a successful authentication. If the user
|
2002-10-25 11:30:33 +00:00
|
|
|
* types the wrong _password_, they can be sent back to the
|
|
|
|
* beginning to try another username, if this is configured on.
|
|
|
|
* (If they specify a username in the config, they are never
|
|
|
|
* asked, even if they do give a wrong password.)
|
2001-03-03 13:53:44 +00:00
|
|
|
*
|
|
|
|
* I think this best serves the needs of
|
|
|
|
*
|
|
|
|
* - the people who have no configuration, no keys, and just
|
|
|
|
* want to try repeated (username,password) pairs until they
|
|
|
|
* type both correctly
|
|
|
|
*
|
|
|
|
* - people who have keys and configuration but occasionally
|
|
|
|
* need to fall back to passwords
|
|
|
|
*
|
|
|
|
* - people with a key held in Pageant, who might not have
|
|
|
|
* logged in to a particular machine before; so they want to
|
|
|
|
* type a username, and then _either_ their key will be
|
|
|
|
* accepted, _or_ they will type a password. If they mistype
|
|
|
|
* the username they will want to be able to get back and
|
|
|
|
* retype it!
|
2000-09-07 16:33:49 +00:00
|
|
|
*/
|
2002-10-25 11:30:33 +00:00
|
|
|
s->got_username = FALSE;
|
2005-05-05 22:37:54 +00:00
|
|
|
while (!s->we_are_in) {
|
2001-03-03 13:53:44 +00:00
|
|
|
/*
|
|
|
|
* Get a username.
|
|
|
|
*/
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (s->got_username && !conf_get_int(ssh->conf, CONF_change_username)) {
|
2001-12-11 21:00:01 +00:00
|
|
|
/*
|
|
|
|
* We got a username last time round this loop, and
|
|
|
|
* with change_username turned off we don't try to get
|
|
|
|
* it again.
|
|
|
|
*/
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
} else if ((ssh->username = get_remote_username(ssh->conf)) == NULL) {
|
2005-10-30 20:24:09 +00:00
|
|
|
s->cur_prompt = new_prompts(ssh->frontend);
|
|
|
|
s->cur_prompt->to_server = TRUE;
|
|
|
|
s->cur_prompt->name = dupstr("SSH login name");
|
2011-10-02 11:50:45 +00:00
|
|
|
add_prompt(s->cur_prompt, dupstr("login as: "), TRUE);
|
2018-05-18 06:22:58 +00:00
|
|
|
s->userpass_ret = get_userpass_input(s->cur_prompt, NULL);
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
while (1) {
|
2018-05-18 06:22:58 +00:00
|
|
|
while (s->userpass_ret < 0 &&
|
|
|
|
bufchain_size(&ssh->user_input) > 0)
|
|
|
|
s->userpass_ret = get_userpass_input(
|
|
|
|
s->cur_prompt, &ssh->user_input);
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
|
|
|
|
if (s->userpass_ret >= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ssh->send_ok = 1;
|
|
|
|
crReturnV;
|
2005-10-30 20:24:09 +00:00
|
|
|
ssh->send_ok = 0;
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
2018-05-18 06:22:58 +00:00
|
|
|
if (!s->userpass_ret) {
|
2005-10-30 20:24:09 +00:00
|
|
|
/*
|
|
|
|
* get_userpass_input() failed to get a username.
|
|
|
|
* Terminate.
|
|
|
|
*/
|
|
|
|
free_prompts(s->cur_prompt);
|
|
|
|
ssh_disconnect(ssh, "No username provided", NULL, 0, TRUE);
|
|
|
|
crStopV;
|
|
|
|
}
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
ssh->username = dupstr(s->cur_prompt->prompts[0]->result);
|
2005-10-30 20:24:09 +00:00
|
|
|
free_prompts(s->cur_prompt);
|
2000-09-07 16:33:49 +00:00
|
|
|
} else {
|
2002-11-07 19:49:03 +00:00
|
|
|
char *stuff;
|
2001-03-03 11:54:34 +00:00
|
|
|
if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
stuff = dupprintf("Using username \"%s\".\r\n", ssh->username);
|
2002-10-25 11:30:33 +00:00
|
|
|
c_write_str(ssh, stuff);
|
2002-11-07 19:49:03 +00:00
|
|
|
sfree(stuff);
|
2000-09-07 16:33:49 +00:00
|
|
|
}
|
|
|
|
}
|
2002-10-25 11:30:33 +00:00
|
|
|
s->got_username = TRUE;
|
2000-09-07 16:33:49 +00:00
|
|
|
|
2001-03-03 11:54:34 +00:00
|
|
|
/*
|
2001-03-03 13:53:44 +00:00
|
|
|
* Send an authentication request using method "none": (a)
|
|
|
|
* just in case it succeeds, and (b) so that we know what
|
|
|
|
* authentication methods we can usefully try next.
|
2001-03-03 11:54:34 +00:00
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->pls.actx = SSH2_PKTCTX_NOAUTH;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, ssh->username);
|
|
|
|
put_stringz(s->pktout, "ssh-connection");/* service requested */
|
|
|
|
put_stringz(s->pktout, "none"); /* method */
|
2004-11-24 18:45:52 +00:00
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
2002-10-25 11:30:33 +00:00
|
|
|
s->type = AUTH_TYPE_NONE;
|
|
|
|
s->we_are_in = FALSE;
|
|
|
|
|
|
|
|
s->tried_pubkey_config = FALSE;
|
2005-01-06 13:23:47 +00:00
|
|
|
s->kbd_inter_refused = FALSE;
|
2001-03-03 11:54:34 +00:00
|
|
|
|
2005-11-13 16:06:41 +00:00
|
|
|
/* Reset agent request state. */
|
|
|
|
s->done_agent = FALSE;
|
|
|
|
if (s->agent_response) {
|
2018-05-27 22:58:20 +00:00
|
|
|
if (s->pkblob_pos_in_agent) {
|
|
|
|
s->asrc->pos = s->pkblob_pos_in_agent;
|
2005-11-13 16:06:41 +00:00
|
|
|
} else {
|
2018-05-27 22:58:20 +00:00
|
|
|
s->asrc->pos = 9; /* skip length + type + key count */
|
2005-11-13 16:06:41 +00:00
|
|
|
s->keyi = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-03-03 13:53:44 +00:00
|
|
|
while (1) {
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen methods;
|
|
|
|
|
|
|
|
methods.ptr = "";
|
|
|
|
methods.len = 0;
|
2010-08-10 18:45:51 +00:00
|
|
|
|
2001-03-03 13:53:44 +00:00
|
|
|
/*
|
|
|
|
* Wait for the result of the last authentication request.
|
|
|
|
*/
|
2018-05-18 06:22:59 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_userauth)) != NULL);
|
2018-05-18 06:22:58 +00:00
|
|
|
|
2005-05-12 15:09:35 +00:00
|
|
|
/*
|
|
|
|
* Now is a convenient point to spew any banner material
|
|
|
|
* that we've accumulated. (This should ensure that when
|
|
|
|
* we exit the auth loop, we haven't any left to deal
|
|
|
|
* with.)
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
int size = bufchain_size(&ssh->banner);
|
2001-05-06 14:35:20 +00:00
|
|
|
/*
|
|
|
|
* Don't show the banner if we're operating in
|
|
|
|
* non-verbose non-interactive mode. (It's probably
|
|
|
|
* a script, which means nobody will read the
|
|
|
|
* banner _anyway_, and moreover the printing of
|
|
|
|
* the banner will screw up processing on the
|
|
|
|
* output of (say) plink.)
|
2018-09-19 17:22:36 +00:00
|
|
|
*
|
|
|
|
* The banner data has been sanitised already by this
|
|
|
|
* point, so we can safely send it straight to the
|
|
|
|
* output channel.
|
2001-05-06 14:35:20 +00:00
|
|
|
*/
|
2005-05-12 15:09:35 +00:00
|
|
|
if (size && (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE))) {
|
|
|
|
char *banner = snewn(size, char);
|
|
|
|
bufchain_fetch(&ssh->banner, banner, size);
|
2018-09-19 17:22:36 +00:00
|
|
|
c_write(ssh, banner, size);
|
2005-05-12 15:09:35 +00:00
|
|
|
sfree(banner);
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
2005-05-12 15:09:35 +00:00
|
|
|
bufchain_clear(&ssh->banner);
|
2001-03-03 13:53:44 +00:00
|
|
|
}
|
2004-11-24 18:45:52 +00:00
|
|
|
if (pktin->type == SSH2_MSG_USERAUTH_SUCCESS) {
|
2001-03-03 13:53:44 +00:00
|
|
|
logevent("Access granted");
|
2011-03-04 22:34:47 +00:00
|
|
|
s->we_are_in = s->userauth_success = TRUE;
|
2001-03-03 13:53:44 +00:00
|
|
|
break;
|
|
|
|
}
|
2001-03-03 11:54:34 +00:00
|
|
|
|
2008-08-10 13:10:31 +00:00
|
|
|
if (pktin->type != SSH2_MSG_USERAUTH_FAILURE && s->type != AUTH_TYPE_GSSAPI) {
|
2005-11-04 14:49:22 +00:00
|
|
|
bombout(("Strange packet received during authentication: "
|
|
|
|
"type %d", pktin->type));
|
2003-04-23 13:48:09 +00:00
|
|
|
crStopV;
|
2001-03-03 11:54:34 +00:00
|
|
|
}
|
|
|
|
|
2001-03-03 13:53:44 +00:00
|
|
|
/*
|
|
|
|
* OK, we're now sitting on a USERAUTH_FAILURE message, so
|
|
|
|
* we can look at the string in it and know what we can
|
|
|
|
* helpfully try next.
|
|
|
|
*/
|
2004-11-24 18:45:52 +00:00
|
|
|
if (pktin->type == SSH2_MSG_USERAUTH_FAILURE) {
|
2018-05-27 17:13:53 +00:00
|
|
|
methods = get_string(pktin);
|
|
|
|
if (!get_bool(pktin)) {
|
2001-03-03 13:53:44 +00:00
|
|
|
/*
|
|
|
|
* We have received an unequivocal Access
|
|
|
|
* Denied. This can translate to a variety of
|
2011-07-16 12:06:32 +00:00
|
|
|
* messages, or no message at all.
|
|
|
|
*
|
|
|
|
* For forms of authentication which are attempted
|
|
|
|
* implicitly, by which I mean without printing
|
|
|
|
* anything in the window indicating that we're
|
|
|
|
* trying them, we should never print 'Access
|
|
|
|
* denied'.
|
|
|
|
*
|
|
|
|
* If we do print a message saying that we're
|
|
|
|
* attempting some kind of authentication, it's OK
|
|
|
|
* to print a followup message saying it failed -
|
|
|
|
* but the message may sometimes be more specific
|
|
|
|
* than simply 'Access denied'.
|
|
|
|
*
|
2001-03-03 13:53:44 +00:00
|
|
|
* Additionally, if we'd just tried password
|
|
|
|
* authentication, we should break out of this
|
|
|
|
* whole loop so as to go back to the username
|
2004-11-20 17:11:38 +00:00
|
|
|
* prompt (iff we're configured to allow
|
|
|
|
* username change attempts).
|
2001-03-03 13:53:44 +00:00
|
|
|
*/
|
2002-10-25 11:30:33 +00:00
|
|
|
if (s->type == AUTH_TYPE_NONE) {
|
2001-03-03 13:53:44 +00:00
|
|
|
/* do nothing */
|
2002-10-25 11:30:33 +00:00
|
|
|
} else if (s->type == AUTH_TYPE_PUBLICKEY_OFFER_LOUD ||
|
|
|
|
s->type == AUTH_TYPE_PUBLICKEY_OFFER_QUIET) {
|
|
|
|
if (s->type == AUTH_TYPE_PUBLICKEY_OFFER_LOUD)
|
|
|
|
c_write_str(ssh, "Server refused our key\r\n");
|
2011-07-16 12:06:32 +00:00
|
|
|
logevent("Server refused our key");
|
|
|
|
} else if (s->type == AUTH_TYPE_PUBLICKEY) {
|
|
|
|
/* This _shouldn't_ happen except by a
|
|
|
|
* protocol bug causing client and server to
|
|
|
|
* disagree on what is a correct signature. */
|
|
|
|
c_write_str(ssh, "Server refused public-key signature"
|
|
|
|
" despite accepting key!\r\n");
|
|
|
|
logevent("Server refused public-key signature"
|
|
|
|
" despite accepting key!");
|
2002-10-25 11:30:33 +00:00
|
|
|
} else if (s->type==AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET) {
|
2011-07-16 12:06:32 +00:00
|
|
|
/* quiet, so no c_write */
|
|
|
|
logevent("Server refused keyboard-interactive authentication");
|
|
|
|
} else if (s->type==AUTH_TYPE_GSSAPI) {
|
|
|
|
/* always quiet, so no c_write */
|
2011-07-18 18:09:36 +00:00
|
|
|
/* also, the code down in the GSSAPI block has
|
|
|
|
* already logged this in the Event Log */
|
2011-07-16 12:06:32 +00:00
|
|
|
} else if (s->type == AUTH_TYPE_KEYBOARD_INTERACTIVE) {
|
|
|
|
logevent("Keyboard-interactive authentication failed");
|
|
|
|
c_write_str(ssh, "Access denied\r\n");
|
|
|
|
} else {
|
|
|
|
assert(s->type == AUTH_TYPE_PASSWORD);
|
|
|
|
logevent("Password authentication failed");
|
2002-10-25 11:30:33 +00:00
|
|
|
c_write_str(ssh, "Access denied\r\n");
|
2011-07-16 12:06:32 +00:00
|
|
|
|
|
|
|
if (conf_get_int(ssh->conf, CONF_change_username)) {
|
2004-11-20 17:47:40 +00:00
|
|
|
/* XXX perhaps we should allow
|
|
|
|
* keyboard-interactive to do this too? */
|
2002-10-25 11:30:33 +00:00
|
|
|
s->we_are_in = FALSE;
|
2001-03-03 13:53:44 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2002-10-25 11:30:33 +00:00
|
|
|
c_write_str(ssh, "Further authentication required\r\n");
|
2001-03-03 13:53:44 +00:00
|
|
|
logevent("Further authentication required");
|
|
|
|
}
|
2001-03-03 11:54:34 +00:00
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
s->can_pubkey =
|
2018-05-27 17:13:53 +00:00
|
|
|
in_commasep_string("publickey", methods.ptr, methods.len);
|
2002-10-25 11:30:33 +00:00
|
|
|
s->can_passwd =
|
2018-05-27 17:13:53 +00:00
|
|
|
in_commasep_string("password", methods.ptr, methods.len);
|
|
|
|
s->can_keyb_inter =
|
|
|
|
conf_get_int(ssh->conf, CONF_try_ki_auth) &&
|
|
|
|
in_commasep_string("keyboard-interactive",
|
|
|
|
methods.ptr, methods.len);
|
2010-09-25 07:16:56 +00:00
|
|
|
#ifndef NO_GSSAPI
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
s->can_gssapi =
|
|
|
|
conf_get_int(ssh->conf, CONF_try_gssapi_auth) &&
|
2018-05-27 17:13:53 +00:00
|
|
|
in_commasep_string("gssapi-with-mic",
|
|
|
|
methods.ptr, methods.len) &&
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
ssh->gsslibs->nlibraries > 0;
|
|
|
|
s->can_gssapi_keyex_auth =
|
2018-04-26 18:15:15 +00:00
|
|
|
conf_get_int(ssh->conf, CONF_try_gssapi_kex) &&
|
2018-05-27 17:13:53 +00:00
|
|
|
in_commasep_string("gssapi-keyex",
|
|
|
|
methods.ptr, methods.len) &&
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
ssh->gsslibs->nlibraries > 0 &&
|
|
|
|
ssh->gss_ctx;
|
2008-08-10 13:10:31 +00:00
|
|
|
#endif
|
2001-03-03 13:53:44 +00:00
|
|
|
}
|
2001-03-03 11:54:34 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->pls.actx = SSH2_PKTCTX_NOAUTH;
|
2001-03-03 11:54:34 +00:00
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
#ifndef NO_GSSAPI
|
|
|
|
if (s->can_gssapi_keyex_auth && !s->tried_gssapi_keyex_auth) {
|
|
|
|
|
|
|
|
/* gssapi-keyex authentication */
|
|
|
|
|
|
|
|
s->type = AUTH_TYPE_GSSAPI;
|
|
|
|
s->tried_gssapi_keyex_auth = TRUE;
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->pls.actx = SSH2_PKTCTX_GSSAPI;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
|
|
|
|
if (ssh->gsslib->gsslogmsg)
|
|
|
|
logevent(ssh->gsslib->gsslogmsg);
|
|
|
|
|
|
|
|
logeventf(ssh, "Trying gssapi-keyex...");
|
|
|
|
s->pktout =
|
|
|
|
ssh2_gss_authpacket(ssh, ssh->gss_ctx, "gssapi-keyex");
|
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
|
|
|
ssh->gsslib->release_cred(ssh->gsslib, &ssh->gss_ctx);
|
|
|
|
ssh->gss_ctx = NULL;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
} else
|
|
|
|
#endif /* NO_GSSAPI */
|
|
|
|
|
2005-11-13 16:06:41 +00:00
|
|
|
if (s->can_pubkey && !s->done_agent && s->nkeys) {
|
2002-08-11 13:02:01 +00:00
|
|
|
|
2001-03-03 15:56:39 +00:00
|
|
|
/*
|
2005-11-13 16:06:41 +00:00
|
|
|
* Attempt public-key authentication using a key from Pageant.
|
2001-03-03 15:56:39 +00:00
|
|
|
*/
|
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->pls.actx = SSH2_PKTCTX_PUBLICKEY;
|
2001-12-14 14:57:50 +00:00
|
|
|
|
2005-11-13 16:06:41 +00:00
|
|
|
logeventf(ssh, "Trying Pageant key #%d", s->keyi);
|
|
|
|
|
|
|
|
/* Unpack key from agent response */
|
2018-05-27 22:58:20 +00:00
|
|
|
s->pk = get_string(s->asrc);
|
|
|
|
s->comment = get_string(s->asrc);
|
|
|
|
{
|
|
|
|
BinarySource src[1];
|
|
|
|
BinarySource_BARE_INIT(src, s->pk.ptr, s->pk.len);
|
|
|
|
s->alg = get_string(src);
|
|
|
|
}
|
2005-11-13 16:06:41 +00:00
|
|
|
|
|
|
|
/* See if server will accept it */
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, ssh->username);
|
|
|
|
put_stringz(s->pktout, "ssh-connection");
|
2005-11-13 16:06:41 +00:00
|
|
|
/* service requested */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, "publickey");
|
2005-11-13 16:06:41 +00:00
|
|
|
/* method */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_bool(s->pktout, FALSE); /* no signature included */
|
2018-05-27 22:58:20 +00:00
|
|
|
put_stringpl(s->pktout, s->alg);
|
|
|
|
put_stringpl(s->pktout, s->pk);
|
2005-11-13 16:06:41 +00:00
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
|
|
|
s->type = AUTH_TYPE_PUBLICKEY_OFFER_QUIET;
|
2001-03-03 15:56:39 +00:00
|
|
|
|
2018-05-18 06:22:59 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_userauth)) != NULL);
|
2005-11-13 16:06:41 +00:00
|
|
|
if (pktin->type != SSH2_MSG_USERAUTH_PK_OK) {
|
2001-03-03 15:56:39 +00:00
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
/* Offer of key refused, presumably via
|
|
|
|
* USERAUTH_FAILURE. Requeue for the next iteration. */
|
2018-05-18 06:22:59 +00:00
|
|
|
pq_push_front(&ssh->pq_ssh2_userauth, pktin);
|
2001-03-03 15:56:39 +00:00
|
|
|
|
2005-11-13 16:06:41 +00:00
|
|
|
} else {
|
2018-05-24 12:18:13 +00:00
|
|
|
strbuf *agentreq, *sigdata;
|
2018-05-27 22:58:20 +00:00
|
|
|
void *r;
|
|
|
|
int rlen;
|
2001-03-03 15:56:39 +00:00
|
|
|
|
2005-11-13 16:06:41 +00:00
|
|
|
if (flags & FLAG_VERBOSE) {
|
|
|
|
c_write_str(ssh, "Authenticating with "
|
|
|
|
"public key \"");
|
2018-05-27 22:58:20 +00:00
|
|
|
c_write(ssh, s->comment.ptr, s->comment.len);
|
2005-11-13 16:06:41 +00:00
|
|
|
c_write_str(ssh, "\" from agent\r\n");
|
|
|
|
}
|
2001-03-03 15:56:39 +00:00
|
|
|
|
2005-11-13 16:06:41 +00:00
|
|
|
/*
|
|
|
|
* Server is willing to accept the key.
|
|
|
|
* Construct a SIGN_REQUEST.
|
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, ssh->username);
|
|
|
|
put_stringz(s->pktout, "ssh-connection");
|
2005-11-13 16:06:41 +00:00
|
|
|
/* service requested */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, "publickey");
|
2005-11-13 16:06:41 +00:00
|
|
|
/* method */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_bool(s->pktout, TRUE); /* signature included */
|
2018-05-27 22:58:20 +00:00
|
|
|
put_stringpl(s->pktout, s->alg);
|
|
|
|
put_stringpl(s->pktout, s->pk);
|
2005-11-13 16:06:41 +00:00
|
|
|
|
|
|
|
/* Ask agent for signature. */
|
2018-05-24 12:18:13 +00:00
|
|
|
agentreq = strbuf_new_for_agent_query();
|
|
|
|
put_byte(agentreq, SSH2_AGENTC_SIGN_REQUEST);
|
2018-05-27 22:58:20 +00:00
|
|
|
put_stringpl(agentreq, s->pk);
|
2005-11-13 16:06:41 +00:00
|
|
|
/* Now the data to be signed... */
|
2018-05-24 12:18:13 +00:00
|
|
|
sigdata = strbuf_new();
|
|
|
|
if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID) {
|
|
|
|
put_data(sigdata, ssh->v2_session_id,
|
|
|
|
ssh->v2_session_id_len);
|
|
|
|
} else {
|
|
|
|
put_string(sigdata, ssh->v2_session_id,
|
|
|
|
ssh->v2_session_id_len);
|
|
|
|
}
|
|
|
|
put_data(sigdata, s->pktout->data + 5,
|
|
|
|
s->pktout->length - 5);
|
|
|
|
put_stringsb(agentreq, sigdata);
|
2005-11-13 16:06:41 +00:00
|
|
|
/* And finally the (zero) flags word. */
|
2018-05-24 12:18:13 +00:00
|
|
|
put_uint32(agentreq, 0);
|
2017-01-29 20:24:15 +00:00
|
|
|
ssh->auth_agent_query = agent_query(
|
2018-05-27 22:58:20 +00:00
|
|
|
agentreq, &r, &rlen, ssh_agent_callback, ssh);
|
2018-05-24 12:18:13 +00:00
|
|
|
strbuf_free(agentreq);
|
|
|
|
|
2017-01-29 20:24:15 +00:00
|
|
|
if (ssh->auth_agent_query) {
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->agent_response = NULL;
|
2018-05-18 06:22:58 +00:00
|
|
|
crWaitUntilV(ssh->agent_response);
|
2018-05-27 22:58:20 +00:00
|
|
|
r = ssh->agent_response;
|
|
|
|
rlen = ssh->agent_response_len;
|
2005-11-13 16:06:41 +00:00
|
|
|
}
|
2018-05-27 22:58:20 +00:00
|
|
|
if (r) {
|
|
|
|
ptrlen sigblob;
|
|
|
|
BinarySource src[1];
|
|
|
|
BinarySource_BARE_INIT(src, r, rlen);
|
|
|
|
get_uint32(src); /* skip length field */
|
|
|
|
if (get_byte(src) == SSH2_AGENT_SIGN_RESPONSE &&
|
|
|
|
(sigblob = get_string(src), !get_err(src))) {
|
2005-11-13 16:06:41 +00:00
|
|
|
logevent("Sending Pageant's response");
|
|
|
|
ssh2_add_sigblob(ssh, s->pktout,
|
2018-05-27 22:58:20 +00:00
|
|
|
s->pk.ptr, s->pk.len,
|
|
|
|
sigblob.ptr, sigblob.len);
|
2005-11-13 16:06:41 +00:00
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
|
|
|
s->type = AUTH_TYPE_PUBLICKEY;
|
|
|
|
} else {
|
|
|
|
/* FIXME: less drastic response */
|
|
|
|
bombout(("Pageant failed to answer challenge"));
|
|
|
|
crStopV;
|
2001-03-03 15:56:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-11-13 16:06:41 +00:00
|
|
|
|
|
|
|
/* Do we have any keys left to try? */
|
2018-05-27 22:58:20 +00:00
|
|
|
if (s->pkblob_pos_in_agent) {
|
2005-11-13 16:06:41 +00:00
|
|
|
s->done_agent = TRUE;
|
|
|
|
s->tried_pubkey_config = TRUE;
|
|
|
|
} else {
|
|
|
|
s->keyi++;
|
|
|
|
if (s->keyi >= s->nkeys)
|
|
|
|
s->done_agent = TRUE;
|
|
|
|
}
|
2001-03-03 15:56:39 +00:00
|
|
|
|
2005-10-30 20:24:09 +00:00
|
|
|
} else if (s->can_pubkey && s->publickey_blob &&
|
2015-05-12 11:30:25 +00:00
|
|
|
s->privatekey_available && !s->tried_pubkey_config) {
|
2001-03-03 11:54:34 +00:00
|
|
|
|
2005-10-30 20:24:09 +00:00
|
|
|
struct ssh2_userkey *key; /* not live over crReturn */
|
|
|
|
char *passphrase; /* not live over crReturn */
|
2001-03-03 11:54:34 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->pls.actx = SSH2_PKTCTX_PUBLICKEY;
|
2001-12-14 14:57:50 +00:00
|
|
|
|
2005-10-30 20:24:09 +00:00
|
|
|
s->tried_pubkey_config = TRUE;
|
|
|
|
|
2001-03-03 11:54:34 +00:00
|
|
|
/*
|
2001-03-03 13:53:44 +00:00
|
|
|
* Try the public key supplied in the configuration.
|
|
|
|
*
|
|
|
|
* First, offer the public blob to see if the server is
|
|
|
|
* willing to accept it.
|
2001-03-03 11:54:34 +00:00
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, ssh->username);
|
|
|
|
put_stringz(s->pktout, "ssh-connection");
|
2005-10-30 20:24:09 +00:00
|
|
|
/* service requested */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, "publickey"); /* method */
|
|
|
|
put_bool(s->pktout, FALSE);
|
2005-10-30 20:24:09 +00:00
|
|
|
/* no signature included */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, s->publickey_algorithm);
|
2018-05-24 09:59:39 +00:00
|
|
|
put_string(s->pktout, s->publickey_blob->s,
|
|
|
|
s->publickey_blob->len);
|
2004-11-24 18:45:52 +00:00
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
2005-10-30 20:24:09 +00:00
|
|
|
logevent("Offered public key");
|
2004-11-24 18:45:52 +00:00
|
|
|
|
2018-05-18 06:22:59 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_userauth)) != NULL);
|
2005-10-30 20:24:09 +00:00
|
|
|
if (pktin->type != SSH2_MSG_USERAUTH_PK_OK) {
|
|
|
|
/* Key refused. Give up. */
|
2018-05-18 06:22:59 +00:00
|
|
|
pq_push_front(&ssh->pq_ssh2_userauth, pktin);
|
2005-10-30 20:24:09 +00:00
|
|
|
s->type = AUTH_TYPE_PUBLICKEY_OFFER_LOUD;
|
|
|
|
continue; /* process this new message */
|
2002-08-03 16:22:55 +00:00
|
|
|
}
|
2005-10-30 20:24:09 +00:00
|
|
|
logevent("Offer of public key accepted");
|
2001-08-28 08:43:33 +00:00
|
|
|
|
2002-08-03 16:22:55 +00:00
|
|
|
/*
|
2005-10-30 20:24:09 +00:00
|
|
|
* Actually attempt a serious authentication using
|
|
|
|
* the key.
|
2002-08-03 16:22:55 +00:00
|
|
|
*/
|
2005-10-30 20:24:09 +00:00
|
|
|
if (flags & FLAG_VERBOSE) {
|
|
|
|
c_write_str(ssh, "Authenticating with public key \"");
|
|
|
|
c_write_str(ssh, s->publickey_comment);
|
|
|
|
c_write_str(ssh, "\"\r\n");
|
|
|
|
}
|
|
|
|
key = NULL;
|
|
|
|
while (!key) {
|
|
|
|
const char *error; /* not live over crReturn */
|
2015-05-12 11:30:25 +00:00
|
|
|
if (s->privatekey_encrypted) {
|
2005-10-30 20:24:09 +00:00
|
|
|
/*
|
|
|
|
* Get a passphrase from the user.
|
|
|
|
*/
|
|
|
|
s->cur_prompt = new_prompts(ssh->frontend);
|
|
|
|
s->cur_prompt->to_server = FALSE;
|
|
|
|
s->cur_prompt->name = dupstr("SSH key passphrase");
|
|
|
|
add_prompt(s->cur_prompt,
|
|
|
|
dupprintf("Passphrase for key \"%.100s\": ",
|
|
|
|
s->publickey_comment),
|
2011-10-02 11:50:45 +00:00
|
|
|
FALSE);
|
2018-05-18 06:22:58 +00:00
|
|
|
s->userpass_ret = get_userpass_input(
|
|
|
|
s->cur_prompt, NULL);
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
while (1) {
|
2018-05-18 06:22:58 +00:00
|
|
|
while (s->userpass_ret < 0 &&
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
bufchain_size(&ssh->user_input) > 0)
|
2018-05-18 06:22:58 +00:00
|
|
|
s->userpass_ret = get_userpass_input(
|
|
|
|
s->cur_prompt, &ssh->user_input);
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
|
|
|
|
if (s->userpass_ret >= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ssh->send_ok = 1;
|
|
|
|
crReturnV;
|
2005-10-30 20:24:09 +00:00
|
|
|
ssh->send_ok = 0;
|
|
|
|
}
|
2018-05-18 06:22:58 +00:00
|
|
|
if (!s->userpass_ret) {
|
2005-10-30 20:24:09 +00:00
|
|
|
/* Failed to get a passphrase. Terminate. */
|
|
|
|
free_prompts(s->cur_prompt);
|
|
|
|
ssh_disconnect(ssh, NULL,
|
|
|
|
"Unable to authenticate",
|
|
|
|
SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,
|
|
|
|
TRUE);
|
|
|
|
crStopV;
|
2004-08-17 14:08:05 +00:00
|
|
|
}
|
2005-10-30 20:24:09 +00:00
|
|
|
passphrase =
|
|
|
|
dupstr(s->cur_prompt->prompts[0]->result);
|
|
|
|
free_prompts(s->cur_prompt);
|
2002-08-03 16:22:55 +00:00
|
|
|
} else {
|
2005-10-30 20:24:09 +00:00
|
|
|
passphrase = NULL; /* no passphrase needed */
|
2002-08-03 16:22:55 +00:00
|
|
|
}
|
2001-03-03 13:53:44 +00:00
|
|
|
|
2005-10-30 20:24:09 +00:00
|
|
|
/*
|
|
|
|
* Try decrypting the key.
|
|
|
|
*/
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
|
|
|
|
key = ssh2_load_userkey(s->keyfile, passphrase, &error);
|
2005-10-30 20:24:09 +00:00
|
|
|
if (passphrase) {
|
|
|
|
/* burn the evidence */
|
2012-07-22 19:51:50 +00:00
|
|
|
smemclr(passphrase, strlen(passphrase));
|
2005-10-30 20:24:09 +00:00
|
|
|
sfree(passphrase);
|
|
|
|
}
|
|
|
|
if (key == SSH2_WRONG_PASSPHRASE || key == NULL) {
|
|
|
|
if (passphrase &&
|
|
|
|
(key == SSH2_WRONG_PASSPHRASE)) {
|
|
|
|
c_write_str(ssh, "Wrong passphrase\r\n");
|
|
|
|
key = NULL;
|
|
|
|
/* and loop again */
|
|
|
|
} else {
|
|
|
|
c_write_str(ssh, "Unable to load private key (");
|
|
|
|
c_write_str(ssh, error);
|
|
|
|
c_write_str(ssh, ")\r\n");
|
|
|
|
key = NULL;
|
|
|
|
break; /* try something else */
|
|
|
|
}
|
2001-03-03 13:53:44 +00:00
|
|
|
}
|
2001-03-03 11:54:34 +00:00
|
|
|
}
|
|
|
|
|
2005-10-30 20:24:09 +00:00
|
|
|
if (key) {
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf *pkblob, *sigdata, *sigblob;
|
2001-03-03 11:54:34 +00:00
|
|
|
|
2001-03-03 13:53:44 +00:00
|
|
|
/*
|
|
|
|
* We have loaded the private key and the server
|
|
|
|
* has announced that it's willing to accept it.
|
|
|
|
* Hallelujah. Generate a signature and send it.
|
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, ssh->username);
|
|
|
|
put_stringz(s->pktout, "ssh-connection");
|
2005-10-30 20:24:09 +00:00
|
|
|
/* service requested */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, "publickey"); /* method */
|
|
|
|
put_bool(s->pktout, TRUE); /* signature follows */
|
2018-06-03 11:58:05 +00:00
|
|
|
put_stringz(s->pktout, ssh_key_ssh_id(key->key));
|
2018-05-24 09:59:39 +00:00
|
|
|
pkblob = strbuf_new();
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh_key_public_blob(key->key, BinarySink_UPCAST(pkblob));
|
2018-05-24 09:59:39 +00:00
|
|
|
put_string(s->pktout, pkblob->s, pkblob->len);
|
2001-03-03 13:53:44 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The data to be signed is:
|
|
|
|
*
|
|
|
|
* string session-id
|
|
|
|
*
|
|
|
|
* followed by everything so far placed in the
|
|
|
|
* outgoing packet.
|
|
|
|
*/
|
2018-05-24 09:59:39 +00:00
|
|
|
sigdata = strbuf_new();
|
|
|
|
if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID) {
|
|
|
|
put_data(sigdata, ssh->v2_session_id,
|
|
|
|
ssh->v2_session_id_len);
|
|
|
|
} else {
|
|
|
|
put_string(sigdata, ssh->v2_session_id,
|
|
|
|
ssh->v2_session_id_len);
|
|
|
|
}
|
|
|
|
put_data(sigdata, s->pktout->data + 5,
|
|
|
|
s->pktout->length - 5);
|
|
|
|
sigblob = strbuf_new();
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh_key_sign(key->key, sigdata->s, sigdata->len,
|
|
|
|
BinarySink_UPCAST(sigblob));
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf_free(sigdata);
|
|
|
|
ssh2_add_sigblob(ssh, s->pktout, pkblob->s, pkblob->len,
|
|
|
|
sigblob->s, sigblob->len);
|
|
|
|
strbuf_free(pkblob);
|
|
|
|
strbuf_free(sigblob);
|
2001-03-03 13:53:44 +00:00
|
|
|
|
2004-11-24 18:45:52 +00:00
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
2011-07-16 12:06:32 +00:00
|
|
|
logevent("Sent public key signature");
|
2002-10-25 11:30:33 +00:00
|
|
|
s->type = AUTH_TYPE_PUBLICKEY;
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh_key_free(key->key);
|
2015-04-26 09:49:24 +00:00
|
|
|
sfree(key->comment);
|
|
|
|
sfree(key);
|
2001-03-03 13:53:44 +00:00
|
|
|
}
|
2005-10-30 20:24:09 +00:00
|
|
|
|
2008-08-10 13:10:31 +00:00
|
|
|
#ifndef NO_GSSAPI
|
|
|
|
} else if (s->can_gssapi && !s->tried_gssapi) {
|
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
/* gssapi-with-mic authentication */
|
2008-08-10 13:10:31 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen data;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
|
2008-08-10 13:10:31 +00:00
|
|
|
s->type = AUTH_TYPE_GSSAPI;
|
|
|
|
s->tried_gssapi = TRUE;
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->pls.actx = SSH2_PKTCTX_GSSAPI;
|
2008-08-10 13:10:31 +00:00
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
if (ssh->gsslib->gsslogmsg)
|
|
|
|
logevent(ssh->gsslib->gsslogmsg);
|
2010-05-19 18:22:17 +00:00
|
|
|
|
2008-08-10 13:10:31 +00:00
|
|
|
/* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
logeventf(ssh, "Trying gssapi-with-mic...");
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, ssh->username);
|
|
|
|
put_stringz(s->pktout, "ssh-connection");
|
|
|
|
put_stringz(s->pktout, "gssapi-with-mic");
|
2011-07-16 12:06:32 +00:00
|
|
|
logevent("Attempting GSSAPI authentication");
|
2008-08-10 13:10:31 +00:00
|
|
|
|
|
|
|
/* add mechanism info */
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
ssh->gsslib->indicate_mech(ssh->gsslib, &s->gss_buf);
|
2008-08-10 13:10:31 +00:00
|
|
|
|
|
|
|
/* number of GSSAPI mechanisms */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(s->pktout, 1);
|
2008-08-10 13:10:31 +00:00
|
|
|
|
|
|
|
/* length of OID + 2 */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(s->pktout, s->gss_buf.length + 2);
|
|
|
|
put_byte(s->pktout, SSH2_GSS_OIDTYPE);
|
2008-08-10 13:10:31 +00:00
|
|
|
|
|
|
|
/* length of OID */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_byte(s->pktout, s->gss_buf.length);
|
2008-08-10 13:10:31 +00:00
|
|
|
|
2018-05-24 09:15:36 +00:00
|
|
|
put_data(s->pktout, s->gss_buf.value, s->gss_buf.length);
|
2008-08-10 13:10:31 +00:00
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
2018-05-18 06:22:59 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_userauth)) != NULL);
|
2008-08-10 13:10:31 +00:00
|
|
|
if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_RESPONSE) {
|
|
|
|
logevent("GSSAPI authentication request refused");
|
2018-05-18 06:22:59 +00:00
|
|
|
pq_push_front(&ssh->pq_ssh2_userauth, pktin);
|
2008-08-10 13:10:31 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check returned packet ... */
|
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
data = get_string(pktin);
|
|
|
|
s->gss_rcvtok.value = (char *)data.ptr;
|
|
|
|
s->gss_rcvtok.length = data.len;
|
2008-11-24 23:44:55 +00:00
|
|
|
if (s->gss_rcvtok.length != s->gss_buf.length + 2 ||
|
|
|
|
((char *)s->gss_rcvtok.value)[0] != SSH2_GSS_OIDTYPE ||
|
|
|
|
((char *)s->gss_rcvtok.value)[1] != s->gss_buf.length ||
|
|
|
|
memcmp((char *)s->gss_rcvtok.value + 2,
|
|
|
|
s->gss_buf.value,s->gss_buf.length) ) {
|
2008-08-10 13:10:31 +00:00
|
|
|
logevent("GSSAPI authentication - wrong response from server");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
/* Import server name if not cached from KEX */
|
|
|
|
if (ssh->gss_srv_name == GSS_C_NO_NAME) {
|
|
|
|
s->gss_stat = ssh->gsslib->import_name(ssh->gsslib,
|
|
|
|
ssh->fullhostname,
|
|
|
|
&ssh->gss_srv_name);
|
|
|
|
if (s->gss_stat != SSH_GSS_OK) {
|
|
|
|
if (s->gss_stat == SSH_GSS_BAD_HOST_NAME)
|
|
|
|
logevent("GSSAPI import name failed -"
|
|
|
|
" Bad service name");
|
|
|
|
else
|
|
|
|
logevent("GSSAPI import name failed");
|
|
|
|
continue;
|
|
|
|
}
|
2008-08-10 13:10:31 +00:00
|
|
|
}
|
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
/* Allocate our gss_ctx */
|
|
|
|
s->gss_stat = ssh->gsslib->acquire_cred(ssh->gsslib,
|
|
|
|
&s->gss_ctx, NULL);
|
2008-08-10 13:10:31 +00:00
|
|
|
if (s->gss_stat != SSH_GSS_OK) {
|
|
|
|
logevent("GSSAPI authentication failed to get credentials");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* initial tokens are empty */
|
2008-11-24 23:44:55 +00:00
|
|
|
SSH_GSS_CLEAR_BUF(&s->gss_rcvtok);
|
2008-11-26 14:30:58 +00:00
|
|
|
SSH_GSS_CLEAR_BUF(&s->gss_sndtok);
|
2008-08-10 13:10:31 +00:00
|
|
|
|
|
|
|
/* now enter the loop */
|
|
|
|
do {
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
/*
|
|
|
|
* When acquire_cred yields no useful expiration, go with
|
|
|
|
* the service ticket expiration.
|
|
|
|
*/
|
|
|
|
s->gss_stat = ssh->gsslib->init_sec_context
|
|
|
|
(ssh->gsslib,
|
2010-05-19 18:22:17 +00:00
|
|
|
&s->gss_ctx,
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
ssh->gss_srv_name,
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
conf_get_int(ssh->conf, CONF_gssapifwd),
|
2010-05-19 18:22:17 +00:00
|
|
|
&s->gss_rcvtok,
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
&s->gss_sndtok,
|
|
|
|
NULL,
|
|
|
|
NULL);
|
2008-08-10 13:10:31 +00:00
|
|
|
|
|
|
|
if (s->gss_stat!=SSH_GSS_S_COMPLETE &&
|
|
|
|
s->gss_stat!=SSH_GSS_S_CONTINUE_NEEDED) {
|
|
|
|
logevent("GSSAPI authentication initialisation failed");
|
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
if (ssh->gsslib->display_status(ssh->gsslib,
|
|
|
|
s->gss_ctx, &s->gss_buf) == SSH_GSS_OK) {
|
2008-11-24 23:44:55 +00:00
|
|
|
logevent(s->gss_buf.value);
|
|
|
|
sfree(s->gss_buf.value);
|
2008-08-10 13:10:31 +00:00
|
|
|
}
|
|
|
|
|
2018-05-25 11:33:14 +00:00
|
|
|
pq_push_front(&ssh->pq_ssh2_userauth, pktin);
|
2008-08-10 13:10:31 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
logevent("GSSAPI authentication initialised");
|
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
/*
|
|
|
|
* Client and server now exchange tokens until GSSAPI
|
|
|
|
* no longer says CONTINUE_NEEDED
|
|
|
|
*/
|
2008-11-24 23:44:55 +00:00
|
|
|
if (s->gss_sndtok.length != 0) {
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
s->pktout =
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_string(s->pktout,
|
|
|
|
s->gss_sndtok.value, s->gss_sndtok.length);
|
2008-08-10 13:10:31 +00:00
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
ssh->gsslib->free_tok(ssh->gsslib, &s->gss_sndtok);
|
2008-08-10 13:10:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED) {
|
2018-05-18 06:22:59 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_userauth)) != NULL);
|
2008-08-10 13:10:31 +00:00
|
|
|
if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_TOKEN) {
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
logevent("GSSAPI authentication -"
|
|
|
|
" bad server response");
|
2008-08-10 13:10:31 +00:00
|
|
|
s->gss_stat = SSH_GSS_FAILURE;
|
2018-05-18 06:22:59 +00:00
|
|
|
pq_push_front(&ssh->pq_ssh2_userauth, pktin);
|
2008-08-10 13:10:31 +00:00
|
|
|
break;
|
|
|
|
}
|
2018-05-27 17:13:53 +00:00
|
|
|
data = get_string(pktin);
|
|
|
|
s->gss_rcvtok.value = (char *)data.ptr;
|
|
|
|
s->gss_rcvtok.length = data.len;
|
2008-08-10 13:10:31 +00:00
|
|
|
}
|
|
|
|
} while (s-> gss_stat == SSH_GSS_S_CONTINUE_NEEDED);
|
|
|
|
|
|
|
|
if (s->gss_stat != SSH_GSS_OK) {
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
ssh->gsslib->release_cred(ssh->gsslib, &s->gss_ctx);
|
2008-08-10 13:10:31 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
logevent("GSSAPI authentication loop finished OK");
|
|
|
|
|
|
|
|
/* Now send the MIC */
|
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
s->pktout =
|
|
|
|
ssh2_gss_authpacket(ssh, s->gss_ctx, "gssapi-with-mic");
|
2008-08-10 13:10:31 +00:00
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
ssh->gsslib->release_cred(ssh->gsslib, &s->gss_ctx);
|
2008-08-10 13:10:31 +00:00
|
|
|
continue;
|
|
|
|
#endif
|
2005-10-30 20:24:09 +00:00
|
|
|
} else if (s->can_keyb_inter && !s->kbd_inter_refused) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Keyboard-interactive authentication.
|
|
|
|
*/
|
|
|
|
|
|
|
|
s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
|
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->pls.actx = SSH2_PKTCTX_KBDINTER;
|
2005-10-30 20:24:09 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, ssh->username);
|
|
|
|
put_stringz(s->pktout, "ssh-connection");
|
2005-10-30 20:24:09 +00:00
|
|
|
/* service requested */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, "keyboard-interactive");
|
2005-10-30 20:24:09 +00:00
|
|
|
/* method */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, ""); /* lang */
|
|
|
|
put_stringz(s->pktout, ""); /* submethods */
|
2005-10-30 20:24:09 +00:00
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
2011-07-16 12:06:32 +00:00
|
|
|
|
|
|
|
logevent("Attempting keyboard-interactive authentication");
|
2005-10-30 20:24:09 +00:00
|
|
|
|
2018-05-18 06:22:59 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_userauth)) != NULL);
|
2005-10-30 20:24:09 +00:00
|
|
|
if (pktin->type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
|
|
|
|
/* Server is not willing to do keyboard-interactive
|
2005-11-09 23:19:33 +00:00
|
|
|
* at all (or, bizarrely but legally, accepts the
|
|
|
|
* user without actually issuing any prompts).
|
|
|
|
* Give up on it entirely. */
|
2018-05-18 06:22:59 +00:00
|
|
|
pq_push_front(&ssh->pq_ssh2_userauth, pktin);
|
2005-10-30 20:24:09 +00:00
|
|
|
s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET;
|
|
|
|
s->kbd_inter_refused = TRUE; /* don't try it again */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-11-09 23:19:33 +00:00
|
|
|
* Loop while the server continues to send INFO_REQUESTs.
|
2005-10-30 20:24:09 +00:00
|
|
|
*/
|
2005-11-09 23:19:33 +00:00
|
|
|
while (pktin->type == SSH2_MSG_USERAUTH_INFO_REQUEST) {
|
2005-10-30 20:24:09 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen name, inst;
|
2005-11-09 23:19:33 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We've got a fresh USERAUTH_INFO_REQUEST.
|
|
|
|
* Get the preamble and start building a prompt.
|
|
|
|
*/
|
2018-05-27 17:13:53 +00:00
|
|
|
name = get_string(pktin);
|
|
|
|
inst = get_string(pktin);
|
|
|
|
get_string(pktin); /* skip language tag */
|
2005-11-09 23:19:33 +00:00
|
|
|
s->cur_prompt = new_prompts(ssh->frontend);
|
|
|
|
s->cur_prompt->to_server = TRUE;
|
2005-10-30 20:24:09 +00:00
|
|
|
|
2005-11-09 23:19:33 +00:00
|
|
|
/*
|
2008-09-09 20:36:40 +00:00
|
|
|
* Get any prompt(s) from the packet.
|
2005-11-09 23:19:33 +00:00
|
|
|
*/
|
2018-05-27 17:13:53 +00:00
|
|
|
s->num_prompts = get_uint32(pktin);
|
2005-11-09 23:19:33 +00:00
|
|
|
for (i = 0; i < s->num_prompts; i++) {
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen prompt;
|
2005-11-09 23:19:33 +00:00
|
|
|
int echo;
|
|
|
|
static char noprompt[] =
|
|
|
|
"<server failed to send prompt>: ";
|
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
prompt = get_string(pktin);
|
|
|
|
echo = get_bool(pktin);
|
|
|
|
if (!prompt.len) {
|
|
|
|
prompt.ptr = noprompt;
|
|
|
|
prompt.len = lenof(noprompt)-1;
|
2005-11-09 23:19:33 +00:00
|
|
|
}
|
2018-05-27 17:13:53 +00:00
|
|
|
add_prompt(s->cur_prompt, mkstr(prompt), echo);
|
2005-10-30 20:24:09 +00:00
|
|
|
}
|
2005-11-09 23:19:33 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
if (name.len) {
|
2009-03-03 19:57:58 +00:00
|
|
|
/* FIXME: better prefix to distinguish from
|
|
|
|
* local prompts? */
|
|
|
|
s->cur_prompt->name =
|
2018-05-27 17:13:53 +00:00
|
|
|
dupprintf("SSH server: %.*s", PTRLEN_PRINTF(name));
|
2009-03-03 19:57:58 +00:00
|
|
|
s->cur_prompt->name_reqd = TRUE;
|
|
|
|
} else {
|
|
|
|
s->cur_prompt->name =
|
|
|
|
dupstr("SSH server authentication");
|
|
|
|
s->cur_prompt->name_reqd = FALSE;
|
|
|
|
}
|
|
|
|
/* We add a prefix to try to make it clear that a prompt
|
|
|
|
* has come from the server.
|
|
|
|
* FIXME: ugly to print "Using..." in prompt _every_
|
|
|
|
* time round. Can this be done more subtly? */
|
|
|
|
/* Special case: for reasons best known to themselves,
|
|
|
|
* some servers send k-i requests with no prompts and
|
|
|
|
* nothing to display. Keep quiet in this case. */
|
2018-05-27 17:13:53 +00:00
|
|
|
if (s->num_prompts || name.len || inst.len) {
|
2009-03-03 19:57:58 +00:00
|
|
|
s->cur_prompt->instruction =
|
2018-05-27 17:13:53 +00:00
|
|
|
dupprintf("Using keyboard-interactive "
|
|
|
|
"authentication.%s%.*s",
|
|
|
|
inst.len ? "\n" : "",
|
|
|
|
PTRLEN_PRINTF(inst));
|
2009-03-03 19:57:58 +00:00
|
|
|
s->cur_prompt->instr_reqd = TRUE;
|
|
|
|
} else {
|
|
|
|
s->cur_prompt->instr_reqd = FALSE;
|
|
|
|
}
|
|
|
|
|
2005-11-09 23:19:33 +00:00
|
|
|
/*
|
2008-09-09 20:36:40 +00:00
|
|
|
* Display any instructions, and get the user's
|
|
|
|
* response(s).
|
2005-11-09 23:19:33 +00:00
|
|
|
*/
|
2018-05-18 06:22:58 +00:00
|
|
|
s->userpass_ret = get_userpass_input(s->cur_prompt, NULL);
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
while (1) {
|
2018-05-18 06:22:58 +00:00
|
|
|
while (s->userpass_ret < 0 &&
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
bufchain_size(&ssh->user_input) > 0)
|
2018-05-18 06:22:58 +00:00
|
|
|
s->userpass_ret = get_userpass_input(
|
|
|
|
s->cur_prompt, &ssh->user_input);
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
|
|
|
|
if (s->userpass_ret >= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ssh->send_ok = 1;
|
|
|
|
crReturnV;
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->send_ok = 0;
|
|
|
|
}
|
|
|
|
if (!s->userpass_ret) {
|
|
|
|
/*
|
|
|
|
* Failed to get responses. Terminate.
|
|
|
|
*/
|
|
|
|
free_prompts(s->cur_prompt);
|
|
|
|
ssh_disconnect(ssh, NULL, "Unable to authenticate",
|
|
|
|
SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,
|
|
|
|
TRUE);
|
|
|
|
crStopV;
|
|
|
|
}
|
2005-11-09 23:19:33 +00:00
|
|
|
|
|
|
|
/*
|
Move password-packet padding into the BPP module.
Now when we construct a packet containing sensitive data, we just set
a field saying '... and make it take up at least this much space, to
disguise its true size', and nothing in the rest of the system worries
about that flag until ssh2bpp.c acts on it.
Also, I've changed the strategy for doing the padding. Previously, we
were following the real packet with an SSH_MSG_IGNORE to make up the
size. But that was only a partial defence: it works OK against passive
traffic analysis, but an attacker proxying the TCP stream and
dribbling it out one byte at a time could still have found out the
size of the real packet by noting when the dribbled data provoked a
response. Now I put the SSH_MSG_IGNORE _first_, which should defeat
that attack.
But that in turn doesn't work when we're doing compression, because we
can't predict the compressed sizes accurately enough to make that
strategy sensible. Fortunately, compression provides an alternative
strategy anyway: if we've got zlib turned on when we send one of these
sensitive packets, then we can pad out the compressed zlib data as
much as we like by adding empty RFC1951 blocks (effectively chaining
ZLIB_PARTIAL_FLUSHes). So both strategies should now be dribble-proof.
2018-07-09 19:30:11 +00:00
|
|
|
* Send the response(s) to the server, padding
|
|
|
|
* them to disguise their true length.
|
2005-11-09 23:19:33 +00:00
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH2_MSG_USERAUTH_INFO_RESPONSE);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(s->pktout, s->num_prompts);
|
2005-11-09 23:19:33 +00:00
|
|
|
for (i=0; i < s->num_prompts; i++) {
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout,
|
|
|
|
s->cur_prompt->prompts[i]->result);
|
2005-11-09 23:19:33 +00:00
|
|
|
}
|
Move password-packet padding into the BPP module.
Now when we construct a packet containing sensitive data, we just set
a field saying '... and make it take up at least this much space, to
disguise its true size', and nothing in the rest of the system worries
about that flag until ssh2bpp.c acts on it.
Also, I've changed the strategy for doing the padding. Previously, we
were following the real packet with an SSH_MSG_IGNORE to make up the
size. But that was only a partial defence: it works OK against passive
traffic analysis, but an attacker proxying the TCP stream and
dribbling it out one byte at a time could still have found out the
size of the real packet by noting when the dribbled data provoked a
response. Now I put the SSH_MSG_IGNORE _first_, which should defeat
that attack.
But that in turn doesn't work when we're doing compression, because we
can't predict the compressed sizes accurately enough to make that
strategy sensible. Fortunately, compression provides an alternative
strategy anyway: if we've got zlib turned on when we send one of these
sensitive packets, then we can pad out the compressed zlib data as
much as we like by adding empty RFC1951 blocks (effectively chaining
ZLIB_PARTIAL_FLUSHes). So both strategies should now be dribble-proof.
2018-07-09 19:30:11 +00:00
|
|
|
s->pktout->minlen = 256;
|
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
2005-11-09 23:19:33 +00:00
|
|
|
|
2011-12-07 19:07:02 +00:00
|
|
|
/*
|
|
|
|
* Free the prompts structure from this iteration.
|
|
|
|
* If there's another, a new one will be allocated
|
|
|
|
* when we return to the top of this while loop.
|
|
|
|
*/
|
|
|
|
free_prompts(s->cur_prompt);
|
|
|
|
|
2005-11-09 23:19:33 +00:00
|
|
|
/*
|
|
|
|
* Get the next packet in case it's another
|
|
|
|
* INFO_REQUEST.
|
|
|
|
*/
|
2018-05-18 06:22:59 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_userauth)) != NULL);
|
2005-11-09 23:19:33 +00:00
|
|
|
|
2005-10-30 20:24:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-11-09 23:19:33 +00:00
|
|
|
* We should have SUCCESS or FAILURE now.
|
2005-10-30 20:24:09 +00:00
|
|
|
*/
|
2018-05-18 06:22:59 +00:00
|
|
|
pq_push_front(&ssh->pq_ssh2_userauth, pktin);
|
2005-10-30 20:24:09 +00:00
|
|
|
|
|
|
|
} else if (s->can_passwd) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Plain old password authentication.
|
|
|
|
*/
|
2005-11-04 14:49:22 +00:00
|
|
|
int changereq_first_time; /* not live over crReturn */
|
2005-10-30 20:24:09 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->pls.actx = SSH2_PKTCTX_PASSWORD;
|
2005-10-30 20:24:09 +00:00
|
|
|
|
|
|
|
s->cur_prompt = new_prompts(ssh->frontend);
|
|
|
|
s->cur_prompt->to_server = TRUE;
|
|
|
|
s->cur_prompt->name = dupstr("SSH password");
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ",
|
|
|
|
ssh->username,
|
2005-10-30 20:24:09 +00:00
|
|
|
ssh->savedhost),
|
2011-10-02 11:50:45 +00:00
|
|
|
FALSE);
|
2005-10-30 20:24:09 +00:00
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
s->userpass_ret = get_userpass_input(s->cur_prompt, NULL);
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
while (1) {
|
2018-05-18 06:22:58 +00:00
|
|
|
while (s->userpass_ret < 0 &&
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
bufchain_size(&ssh->user_input) > 0)
|
2018-05-18 06:22:58 +00:00
|
|
|
s->userpass_ret = get_userpass_input(
|
|
|
|
s->cur_prompt, &ssh->user_input);
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
|
|
|
|
if (s->userpass_ret >= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ssh->send_ok = 1;
|
|
|
|
crReturnV;
|
2005-10-30 20:24:09 +00:00
|
|
|
ssh->send_ok = 0;
|
|
|
|
}
|
2018-05-18 06:22:58 +00:00
|
|
|
if (!s->userpass_ret) {
|
2005-10-30 20:24:09 +00:00
|
|
|
/*
|
|
|
|
* Failed to get responses. Terminate.
|
|
|
|
*/
|
|
|
|
free_prompts(s->cur_prompt);
|
|
|
|
ssh_disconnect(ssh, NULL, "Unable to authenticate",
|
|
|
|
SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,
|
|
|
|
TRUE);
|
|
|
|
crStopV;
|
|
|
|
}
|
2005-11-04 14:49:22 +00:00
|
|
|
/*
|
|
|
|
* Squirrel away the password. (We may need it later if
|
|
|
|
* asked to change it.)
|
|
|
|
*/
|
|
|
|
s->password = dupstr(s->cur_prompt->prompts[0]->result);
|
|
|
|
free_prompts(s->cur_prompt);
|
2005-10-30 20:24:09 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Send the password packet.
|
|
|
|
*
|
2005-01-17 00:50:35 +00:00
|
|
|
* We pad out the password packet to 256 bytes to make
|
|
|
|
* it harder for an attacker to find the length of the
|
|
|
|
* user's password.
|
2001-03-03 13:53:44 +00:00
|
|
|
*
|
2005-01-17 00:50:35 +00:00
|
|
|
* Anyone using a password longer than 256 bytes
|
|
|
|
* probably doesn't have much to worry about from
|
2001-03-03 13:53:44 +00:00
|
|
|
* people who find out how long their password is!
|
2001-03-03 11:54:34 +00:00
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, ssh->username);
|
|
|
|
put_stringz(s->pktout, "ssh-connection");
|
2005-10-30 20:24:09 +00:00
|
|
|
/* service requested */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, "password");
|
|
|
|
put_bool(s->pktout, FALSE);
|
|
|
|
put_stringz(s->pktout, s->password);
|
Move password-packet padding into the BPP module.
Now when we construct a packet containing sensitive data, we just set
a field saying '... and make it take up at least this much space, to
disguise its true size', and nothing in the rest of the system worries
about that flag until ssh2bpp.c acts on it.
Also, I've changed the strategy for doing the padding. Previously, we
were following the real packet with an SSH_MSG_IGNORE to make up the
size. But that was only a partial defence: it works OK against passive
traffic analysis, but an attacker proxying the TCP stream and
dribbling it out one byte at a time could still have found out the
size of the real packet by noting when the dribbled data provoked a
response. Now I put the SSH_MSG_IGNORE _first_, which should defeat
that attack.
But that in turn doesn't work when we're doing compression, because we
can't predict the compressed sizes accurately enough to make that
strategy sensible. Fortunately, compression provides an alternative
strategy anyway: if we've got zlib turned on when we send one of these
sensitive packets, then we can pad out the compressed zlib data as
much as we like by adding empty RFC1951 blocks (effectively chaining
ZLIB_PARTIAL_FLUSHes). So both strategies should now be dribble-proof.
2018-07-09 19:30:11 +00:00
|
|
|
s->pktout->minlen = 256;
|
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
2001-03-03 16:05:52 +00:00
|
|
|
logevent("Sent password");
|
2005-10-31 22:43:51 +00:00
|
|
|
s->type = AUTH_TYPE_PASSWORD;
|
2005-10-30 20:24:09 +00:00
|
|
|
|
2005-11-04 14:49:22 +00:00
|
|
|
/*
|
|
|
|
* Wait for next packet, in case it's a password change
|
|
|
|
* request.
|
|
|
|
*/
|
2018-05-18 06:22:59 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_userauth)) != NULL);
|
2005-11-04 14:49:22 +00:00
|
|
|
changereq_first_time = TRUE;
|
|
|
|
|
|
|
|
while (pktin->type == SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We're being asked for a new password
|
|
|
|
* (perhaps not for the first time).
|
|
|
|
* Loop until the server accepts it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int got_new = FALSE; /* not live over crReturn */
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen prompt; /* not live over crReturn */
|
2005-11-04 14:49:22 +00:00
|
|
|
|
|
|
|
{
|
2015-05-15 10:15:42 +00:00
|
|
|
const char *msg;
|
2005-11-04 14:49:22 +00:00
|
|
|
if (changereq_first_time)
|
|
|
|
msg = "Server requested password change";
|
|
|
|
else
|
|
|
|
msg = "Server rejected new password";
|
|
|
|
logevent(msg);
|
|
|
|
c_write_str(ssh, msg);
|
|
|
|
c_write_str(ssh, "\r\n");
|
|
|
|
}
|
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
prompt = get_string(pktin);
|
2005-11-04 14:49:22 +00:00
|
|
|
|
|
|
|
s->cur_prompt = new_prompts(ssh->frontend);
|
|
|
|
s->cur_prompt->to_server = TRUE;
|
|
|
|
s->cur_prompt->name = dupstr("New SSH password");
|
2018-05-27 17:13:53 +00:00
|
|
|
s->cur_prompt->instruction = mkstr(prompt);
|
2005-11-04 14:49:22 +00:00
|
|
|
s->cur_prompt->instr_reqd = TRUE;
|
2006-09-05 21:39:30 +00:00
|
|
|
/*
|
|
|
|
* There's no explicit requirement in the protocol
|
|
|
|
* for the "old" passwords in the original and
|
|
|
|
* password-change messages to be the same, and
|
|
|
|
* apparently some Cisco kit supports password change
|
|
|
|
* by the user entering a blank password originally
|
|
|
|
* and the real password subsequently, so,
|
|
|
|
* reluctantly, we prompt for the old password again.
|
|
|
|
*
|
|
|
|
* (On the other hand, some servers don't even bother
|
|
|
|
* to check this field.)
|
|
|
|
*/
|
|
|
|
add_prompt(s->cur_prompt,
|
|
|
|
dupstr("Current password (blank for previously entered password): "),
|
2011-10-02 11:50:45 +00:00
|
|
|
FALSE);
|
2005-11-04 14:49:22 +00:00
|
|
|
add_prompt(s->cur_prompt, dupstr("Enter new password: "),
|
2011-10-02 11:50:45 +00:00
|
|
|
FALSE);
|
2005-11-04 14:49:22 +00:00
|
|
|
add_prompt(s->cur_prompt, dupstr("Confirm new password: "),
|
2011-10-02 11:50:45 +00:00
|
|
|
FALSE);
|
2005-11-04 14:49:22 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Loop until the user manages to enter the same
|
|
|
|
* password twice.
|
|
|
|
*/
|
|
|
|
while (!got_new) {
|
2018-05-18 06:22:58 +00:00
|
|
|
s->userpass_ret = get_userpass_input(
|
|
|
|
s->cur_prompt, NULL);
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
while (1) {
|
2018-05-18 06:22:58 +00:00
|
|
|
while (s->userpass_ret < 0 &&
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
bufchain_size(&ssh->user_input) > 0)
|
2018-05-18 06:22:58 +00:00
|
|
|
s->userpass_ret = get_userpass_input(
|
|
|
|
s->cur_prompt, &ssh->user_input);
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
|
|
|
|
if (s->userpass_ret >= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ssh->send_ok = 1;
|
|
|
|
crReturnV;
|
2005-11-04 14:49:22 +00:00
|
|
|
ssh->send_ok = 0;
|
|
|
|
}
|
2018-05-18 06:22:58 +00:00
|
|
|
if (!s->userpass_ret) {
|
2005-11-04 14:49:22 +00:00
|
|
|
/*
|
|
|
|
* Failed to get responses. Terminate.
|
|
|
|
*/
|
|
|
|
/* burn the evidence */
|
|
|
|
free_prompts(s->cur_prompt);
|
2012-07-22 19:51:50 +00:00
|
|
|
smemclr(s->password, strlen(s->password));
|
2005-11-04 14:49:22 +00:00
|
|
|
sfree(s->password);
|
|
|
|
ssh_disconnect(ssh, NULL, "Unable to authenticate",
|
|
|
|
SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,
|
|
|
|
TRUE);
|
|
|
|
crStopV;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2006-09-05 21:39:30 +00:00
|
|
|
* If the user specified a new original password
|
|
|
|
* (IYSWIM), overwrite any previously specified
|
|
|
|
* one.
|
|
|
|
* (A side effect is that the user doesn't have to
|
|
|
|
* re-enter it if they louse up the new password.)
|
|
|
|
*/
|
|
|
|
if (s->cur_prompt->prompts[0]->result[0]) {
|
2012-07-22 19:51:50 +00:00
|
|
|
smemclr(s->password, strlen(s->password));
|
2006-09-05 21:39:30 +00:00
|
|
|
/* burn the evidence */
|
|
|
|
sfree(s->password);
|
|
|
|
s->password =
|
|
|
|
dupstr(s->cur_prompt->prompts[0]->result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check the two new passwords match.
|
2005-11-04 14:49:22 +00:00
|
|
|
*/
|
2006-09-05 21:39:30 +00:00
|
|
|
got_new = (strcmp(s->cur_prompt->prompts[1]->result,
|
|
|
|
s->cur_prompt->prompts[2]->result)
|
2005-11-04 14:49:22 +00:00
|
|
|
== 0);
|
|
|
|
if (!got_new)
|
|
|
|
/* They don't. Silly user. */
|
|
|
|
c_write_str(ssh, "Passwords do not match\r\n");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send the new password (along with the old one).
|
|
|
|
* (see above for padding rationale)
|
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
s->pktout = ssh_bpp_new_pktout(
|
|
|
|
ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, ssh->username);
|
|
|
|
put_stringz(s->pktout, "ssh-connection");
|
2005-11-04 14:49:22 +00:00
|
|
|
/* service requested */
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, "password");
|
|
|
|
put_bool(s->pktout, TRUE);
|
|
|
|
put_stringz(s->pktout, s->password);
|
|
|
|
put_stringz(s->pktout,
|
2006-09-05 21:39:30 +00:00
|
|
|
s->cur_prompt->prompts[1]->result);
|
2005-11-04 14:49:22 +00:00
|
|
|
free_prompts(s->cur_prompt);
|
Move password-packet padding into the BPP module.
Now when we construct a packet containing sensitive data, we just set
a field saying '... and make it take up at least this much space, to
disguise its true size', and nothing in the rest of the system worries
about that flag until ssh2bpp.c acts on it.
Also, I've changed the strategy for doing the padding. Previously, we
were following the real packet with an SSH_MSG_IGNORE to make up the
size. But that was only a partial defence: it works OK against passive
traffic analysis, but an attacker proxying the TCP stream and
dribbling it out one byte at a time could still have found out the
size of the real packet by noting when the dribbled data provoked a
response. Now I put the SSH_MSG_IGNORE _first_, which should defeat
that attack.
But that in turn doesn't work when we're doing compression, because we
can't predict the compressed sizes accurately enough to make that
strategy sensible. Fortunately, compression provides an alternative
strategy anyway: if we've got zlib turned on when we send one of these
sensitive packets, then we can pad out the compressed zlib data as
much as we like by adding empty RFC1951 blocks (effectively chaining
ZLIB_PARTIAL_FLUSHes). So both strategies should now be dribble-proof.
2018-07-09 19:30:11 +00:00
|
|
|
s->pktout->minlen = 256;
|
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
2005-11-04 14:49:22 +00:00
|
|
|
logevent("Sent new password");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now see what the server has to say about it.
|
|
|
|
* (If it's CHANGEREQ again, it's not happy with the
|
|
|
|
* new password.)
|
|
|
|
*/
|
2018-05-18 06:22:59 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_userauth)) != NULL);
|
2005-11-04 14:49:22 +00:00
|
|
|
changereq_first_time = FALSE;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We need to reexamine the current pktin at the top
|
|
|
|
* of the loop. Either:
|
|
|
|
* - we weren't asked to change password at all, in
|
|
|
|
* which case it's a SUCCESS or FAILURE with the
|
|
|
|
* usual meaning
|
|
|
|
* - we sent a new password, and the server was
|
|
|
|
* either OK with it (SUCCESS or FAILURE w/partial
|
|
|
|
* success) or unhappy with the _old_ password
|
|
|
|
* (FAILURE w/o partial success)
|
|
|
|
* In any of these cases, we go back to the top of
|
|
|
|
* the loop and start again.
|
|
|
|
*/
|
2018-05-18 06:22:59 +00:00
|
|
|
pq_push_front(&ssh->pq_ssh2_userauth, pktin);
|
2005-11-04 14:49:22 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We don't need the old password any more, in any
|
|
|
|
* case. Burn the evidence.
|
|
|
|
*/
|
2012-07-22 19:51:50 +00:00
|
|
|
smemclr(s->password, strlen(s->password));
|
2005-11-04 14:49:22 +00:00
|
|
|
sfree(s->password);
|
|
|
|
|
2001-03-03 13:53:44 +00:00
|
|
|
} else {
|
2018-05-27 17:13:53 +00:00
|
|
|
char *str = dupprintf(
|
|
|
|
"No supported authentication methods available"
|
|
|
|
" (server sent: %.*s)", PTRLEN_PRINTF(methods));
|
2005-10-30 20:24:09 +00:00
|
|
|
|
2010-08-10 18:45:51 +00:00
|
|
|
ssh_disconnect(ssh, str,
|
2005-05-21 16:49:27 +00:00
|
|
|
"No supported authentication methods available",
|
|
|
|
SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
|
|
|
|
FALSE);
|
2010-08-10 18:45:51 +00:00
|
|
|
sfree(str);
|
|
|
|
|
2003-04-23 13:48:09 +00:00
|
|
|
crStopV;
|
2005-10-30 20:24:09 +00:00
|
|
|
|
2001-03-03 11:54:34 +00:00
|
|
|
}
|
2005-10-30 20:24:09 +00:00
|
|
|
|
2001-03-03 11:54:34 +00:00
|
|
|
}
|
2005-05-05 22:37:54 +00:00
|
|
|
}
|
2005-05-12 15:09:35 +00:00
|
|
|
ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = NULL;
|
2000-09-07 16:33:49 +00:00
|
|
|
|
2005-10-30 20:24:09 +00:00
|
|
|
/* Clear up various bits and pieces from authentication. */
|
|
|
|
if (s->publickey_blob) {
|
2015-05-15 09:12:06 +00:00
|
|
|
sfree(s->publickey_algorithm);
|
2018-05-24 09:59:39 +00:00
|
|
|
strbuf_free(s->publickey_blob);
|
2005-10-30 20:24:09 +00:00
|
|
|
sfree(s->publickey_comment);
|
|
|
|
}
|
2005-11-13 16:06:41 +00:00
|
|
|
if (s->agent_response)
|
|
|
|
sfree(s->agent_response);
|
2005-10-30 20:24:09 +00:00
|
|
|
|
2018-05-18 06:22:59 +00:00
|
|
|
if (s->userauth_success) {
|
2011-03-04 22:34:47 +00:00
|
|
|
/*
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
* We've just received USERAUTH_SUCCESS, and we haven't sent
|
|
|
|
* any packets since. Signal the transport layer to consider
|
|
|
|
* doing an immediate rekey, if it has any reason to want to.
|
2011-03-04 22:34:47 +00:00
|
|
|
*
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
* (Relying on we_are_in is not sufficient. One of the reasons
|
|
|
|
* to do a post-userauth rekey is OpenSSH delayed compression;
|
|
|
|
* draft-miller-secsh-compression-delayed is quite clear that
|
|
|
|
* that triggers on USERAUTH_SUCCESS specifically, and
|
|
|
|
* we_are_in can become set for other reasons.)
|
2011-03-04 22:34:47 +00:00
|
|
|
*/
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->rekey_reason = NULL; /* will be filled in later */
|
|
|
|
ssh->rekey_class = RK_POST_USERAUTH;
|
2018-05-18 06:22:58 +00:00
|
|
|
queue_idempotent_callback(&ssh->ssh2_transport_icb);
|
2011-03-04 22:34:47 +00:00
|
|
|
}
|
|
|
|
|
2018-05-18 06:22:59 +00:00
|
|
|
/*
|
|
|
|
* Finally, hand over to the connection layer.
|
|
|
|
*/
|
|
|
|
do_ssh2_connection(ssh);
|
|
|
|
ssh->current_user_input_fn = ssh2_connection_input;
|
2018-05-18 10:40:21 +00:00
|
|
|
queue_idempotent_callback(&ssh->user_input_consumer);
|
2018-05-18 06:22:59 +00:00
|
|
|
|
|
|
|
crFinishV;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ssh2_userauth_input(Ssh ssh)
|
|
|
|
{
|
|
|
|
do_ssh2_userauth(ssh);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle the SSH-2 connection layer.
|
|
|
|
*/
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_connection(Ssh ssh, PktIn *pktin)
|
2018-05-18 06:22:59 +00:00
|
|
|
{
|
|
|
|
pktin->refcount++; /* avoid packet being freed when we return */
|
|
|
|
pq_push(&ssh->pq_ssh2_connection, pktin);
|
|
|
|
queue_idempotent_callback(&ssh->ssh2_connection_icb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ssh2_response_connection(struct ssh_channel *c,
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktIn *pktin, void *ctx)
|
2018-05-18 06:22:59 +00:00
|
|
|
{
|
|
|
|
if (pktin)
|
|
|
|
ssh2_msg_connection(c->ssh, pktin);
|
|
|
|
}
|
|
|
|
|
2018-05-31 17:42:47 +00:00
|
|
|
static void ssh2_connection_setup(Ssh ssh)
|
2018-05-18 06:22:59 +00:00
|
|
|
{
|
2018-05-31 17:42:47 +00:00
|
|
|
/*
|
|
|
|
* Initially, most connection-protocol messages go to the function
|
|
|
|
* that queues them for handling by the main do_ssh2_connection
|
|
|
|
* coroutine.
|
|
|
|
*/
|
2018-05-18 06:22:59 +00:00
|
|
|
ssh->packet_dispatch[SSH2_MSG_REQUEST_SUCCESS] = ssh2_msg_connection;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_REQUEST_FAILURE] = ssh2_msg_connection;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_connection;
|
2018-05-31 17:42:47 +00:00
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] =
|
|
|
|
ssh2_msg_connection;
|
2018-05-18 06:22:59 +00:00
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] = ssh2_msg_connection;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] = ssh2_msg_connection;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = ssh2_msg_connection;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = ssh2_msg_connection;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = ssh2_msg_connection;
|
|
|
|
|
2004-11-24 20:35:15 +00:00
|
|
|
/*
|
2018-05-31 17:42:47 +00:00
|
|
|
* But a couple of them are easier to pass to special handler
|
|
|
|
* functions right from the start.
|
2004-11-24 20:35:15 +00:00
|
|
|
*/
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] =
|
|
|
|
ssh2_msg_channel_window_adjust;
|
2004-11-27 15:32:45 +00:00
|
|
|
ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] =
|
|
|
|
ssh2_msg_global_request;
|
2004-11-24 20:35:15 +00:00
|
|
|
|
2018-05-31 17:42:47 +00:00
|
|
|
/*
|
|
|
|
* Ensure our channels tree exists.
|
|
|
|
*/
|
|
|
|
if (!ssh->channels)
|
|
|
|
ssh->channels = newtree234(ssh_channelcmp);
|
|
|
|
}
|
|
|
|
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
typedef struct mainchan {
|
|
|
|
Ssh ssh;
|
2018-09-14 12:47:13 +00:00
|
|
|
SshChannel *sc;
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
|
|
|
|
Channel chan;
|
|
|
|
} mainchan;
|
|
|
|
|
|
|
|
static void mainchan_free(Channel *chan);
|
|
|
|
static void mainchan_open_confirmation(Channel *chan);
|
|
|
|
static void mainchan_open_failure(Channel *chan, const char *errtext);
|
|
|
|
static int mainchan_send(Channel *chan, int is_stderr, const void *, int);
|
|
|
|
static void mainchan_send_eof(Channel *chan);
|
|
|
|
static void mainchan_set_input_wanted(Channel *chan, int wanted);
|
|
|
|
static char *mainchan_log_close_msg(Channel *chan);
|
|
|
|
|
|
|
|
static const struct ChannelVtable mainchan_channelvt = {
|
|
|
|
mainchan_free,
|
|
|
|
mainchan_open_confirmation,
|
|
|
|
mainchan_open_failure,
|
|
|
|
mainchan_send,
|
|
|
|
mainchan_send_eof,
|
|
|
|
mainchan_set_input_wanted,
|
|
|
|
mainchan_log_close_msg,
|
|
|
|
chan_no_eager_close,
|
|
|
|
};
|
|
|
|
|
|
|
|
static mainchan *mainchan_new(Ssh ssh)
|
|
|
|
{
|
|
|
|
mainchan *mc = snew(mainchan);
|
|
|
|
mc->ssh = ssh;
|
2018-09-14 12:47:13 +00:00
|
|
|
mc->sc = NULL;
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
mc->chan.vt = &mainchan_channelvt;
|
|
|
|
mc->chan.initial_fixed_window_size = 0;
|
|
|
|
return mc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mainchan_free(Channel *chan)
|
|
|
|
{
|
|
|
|
assert(chan->vt == &mainchan_channelvt);
|
|
|
|
mainchan *mc = FROMFIELD(chan, mainchan, chan);
|
|
|
|
mc->ssh->mainchan = NULL;
|
|
|
|
sfree(mc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mainchan_open_confirmation(Channel *chan)
|
|
|
|
{
|
|
|
|
assert(FALSE && "OPEN_CONFIRMATION for main channel should be "
|
|
|
|
"handled by connection layer setup");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mainchan_open_failure(Channel *chan, const char *errtext)
|
|
|
|
{
|
|
|
|
assert(FALSE && "OPEN_FAILURE for main channel should be "
|
|
|
|
"handled by connection layer setup");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mainchan_send(Channel *chan, int is_stderr,
|
|
|
|
const void *data, int length)
|
|
|
|
{
|
|
|
|
assert(chan->vt == &mainchan_channelvt);
|
|
|
|
mainchan *mc = FROMFIELD(chan, mainchan, chan);
|
|
|
|
return from_backend(mc->ssh->frontend, is_stderr, data, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mainchan_send_eof(Channel *chan)
|
|
|
|
{
|
|
|
|
assert(chan->vt == &mainchan_channelvt);
|
|
|
|
mainchan *mc = FROMFIELD(chan, mainchan, chan);
|
|
|
|
|
|
|
|
if (!mc->ssh->sent_console_eof &&
|
|
|
|
(from_backend_eof(mc->ssh->frontend) || mc->ssh->got_pty)) {
|
|
|
|
/*
|
|
|
|
* Either from_backend_eof told us that the front end wants us
|
|
|
|
* to close the outgoing side of the connection as soon as we
|
|
|
|
* see EOF from the far end, or else we've unilaterally
|
|
|
|
* decided to do that because we've allocated a remote pty and
|
|
|
|
* hence EOF isn't a particularly meaningful concept.
|
|
|
|
*/
|
2018-09-14 12:47:13 +00:00
|
|
|
sshfwd_write_eof(mc->sc);
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
}
|
|
|
|
mc->ssh->sent_console_eof = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mainchan_set_input_wanted(Channel *chan, int wanted)
|
|
|
|
{
|
|
|
|
assert(chan->vt == &mainchan_channelvt);
|
|
|
|
mainchan *mc = FROMFIELD(chan, mainchan, chan);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is the main channel of the SSH session, i.e. the one tied
|
|
|
|
* to the standard input (or GUI) of the primary SSH client user
|
|
|
|
* interface. So ssh->send_ok is how we control whether we're
|
|
|
|
* reading from that input.
|
|
|
|
*/
|
|
|
|
mc->ssh->send_ok = wanted;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *mainchan_log_close_msg(Channel *chan)
|
|
|
|
{
|
|
|
|
return dupstr("Main session channel closed");
|
|
|
|
}
|
|
|
|
|
2018-05-31 17:42:47 +00:00
|
|
|
static void do_ssh2_connection(void *vctx)
|
|
|
|
{
|
|
|
|
Ssh ssh = (Ssh)vctx;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktIn *pktin;
|
2018-05-31 17:42:47 +00:00
|
|
|
|
|
|
|
struct do_ssh2_connection_state {
|
|
|
|
int crLine;
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
2018-05-31 17:42:47 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
crState(do_ssh2_connection_state);
|
|
|
|
|
|
|
|
crBeginState;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialise our dispatch table entries. Normally this will have
|
|
|
|
* been done as a side effect of USERAUTH_SUCCESS, but in some
|
|
|
|
* cases, it might not (e.g. if we bypassed userauth, or if we're
|
|
|
|
* running the bare-connection protocol).
|
|
|
|
*/
|
|
|
|
ssh2_connection_setup(ssh);
|
|
|
|
|
2000-09-07 16:33:49 +00:00
|
|
|
/*
|
2004-10-13 13:43:11 +00:00
|
|
|
* Create the main session channel.
|
2000-09-07 16:33:49 +00:00
|
|
|
*/
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (conf_get_int(ssh->conf, CONF_ssh_no_shell)) {
|
2006-08-28 15:12:37 +00:00
|
|
|
ssh->mainchan = NULL;
|
2012-09-08 19:46:07 +00:00
|
|
|
} else {
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
mainchan *mc = mainchan_new(ssh);
|
2006-08-28 15:12:37 +00:00
|
|
|
|
2012-09-08 19:46:07 +00:00
|
|
|
if (*conf_get_str(ssh->conf, CONF_ssh_nc_host)) {
|
|
|
|
/*
|
|
|
|
* Just start a direct-tcpip channel and use it as the main
|
|
|
|
* channel.
|
|
|
|
*/
|
2018-09-17 11:14:00 +00:00
|
|
|
mc->sc = ssh_lportfwd_open
|
|
|
|
(&ssh->cl, conf_get_str(ssh->conf, CONF_ssh_nc_host),
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
conf_get_int(ssh->conf, CONF_ssh_nc_port),
|
|
|
|
"main channel", &mc->chan);
|
2018-09-14 12:47:13 +00:00
|
|
|
ssh->mainchan = FROMFIELD(mc->sc, struct ssh_channel, sc);
|
2012-09-08 19:46:07 +00:00
|
|
|
ssh->ncmode = TRUE;
|
|
|
|
} else {
|
2018-09-14 12:47:13 +00:00
|
|
|
ssh->mainchan = snew(struct ssh_channel);
|
|
|
|
mc->sc = &ssh->mainchan->sc;
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
ssh->mainchan->ssh = ssh;
|
|
|
|
ssh_channel_init(ssh->mainchan);
|
|
|
|
ssh->mainchan->chan = &mc->chan;
|
2012-09-08 19:46:07 +00:00
|
|
|
s->pktout = ssh2_chanopen_init(ssh->mainchan, "session");
|
|
|
|
logevent("Opening session as main channel");
|
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
|
|
|
ssh->ncmode = FALSE;
|
2006-08-28 15:12:37 +00:00
|
|
|
}
|
2018-05-18 06:22:59 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_connection)) != NULL);
|
Log the server's diagnostics if main channel open fails.
This has been a FIXME in the code for ages, because back when the main
channel was always a pty session or a program run in a pipe, there
weren't that many circumstances in which the actual CHANNEL_OPEN could
return failure, so it never seemed like a priority to get round to
pulling the error information out of the CHANNEL_OPEN_FAILURE response
message and including it in PuTTY or Plink's local error message.
However, 'plink -nc' is the real reason why this is actually
important; if you tell the SSH server to make a direct-tcpip network
connection as its main channel, then that can fail for all the usual
network-unreliability reasons, and you actually do want to know which
(did you misspell the hostname, or is the target server refusing
connections, or has network connectivity failed?). This actually bit
me today when I had such a network failure, and had to debug it by
pulling that information manually out of a packet log. Time to
eliminate that FIXME.
So I've pulled the error-extracting code out of the previous handler
for OPEN_FAILURE on non-main channels into a separate function, and
arranged to call that function if the main channel open fails too. In
the process I've made a couple of minor tweaks, e.g. if the server
sends back a reason code we haven't heard of, we say _what_ that
reason code was, and also we at least make a token effort to spot if
we see a packet other than OPEN_{CONFIRMATION,FAILURE} reaching the
main loop in response to the main channel-open.
2017-06-15 17:58:01 +00:00
|
|
|
if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION &&
|
|
|
|
pktin->type != SSH2_MSG_CHANNEL_OPEN_FAILURE) {
|
|
|
|
bombout(("Server sent strange packet %d in response to main "
|
|
|
|
"channel open request", pktin->type));
|
2004-10-13 13:43:11 +00:00
|
|
|
crStopV;
|
Log the server's diagnostics if main channel open fails.
This has been a FIXME in the code for ages, because back when the main
channel was always a pty session or a program run in a pipe, there
weren't that many circumstances in which the actual CHANNEL_OPEN could
return failure, so it never seemed like a priority to get round to
pulling the error information out of the CHANNEL_OPEN_FAILURE response
message and including it in PuTTY or Plink's local error message.
However, 'plink -nc' is the real reason why this is actually
important; if you tell the SSH server to make a direct-tcpip network
connection as its main channel, then that can fail for all the usual
network-unreliability reasons, and you actually do want to know which
(did you misspell the hostname, or is the target server refusing
connections, or has network connectivity failed?). This actually bit
me today when I had such a network failure, and had to debug it by
pulling that information manually out of a packet log. Time to
eliminate that FIXME.
So I've pulled the error-extracting code out of the previous handler
for OPEN_FAILURE on non-main channels into a separate function, and
arranged to call that function if the main channel open fails too. In
the process I've made a couple of minor tweaks, e.g. if the server
sends back a reason code we haven't heard of, we say _what_ that
reason code was, and also we at least make a token effort to spot if
we see a packet other than OPEN_{CONFIRMATION,FAILURE} reaching the
main loop in response to the main channel-open.
2017-06-15 17:58:01 +00:00
|
|
|
}
|
2018-05-27 17:13:53 +00:00
|
|
|
if (get_uint32(pktin) != ssh->mainchan->localid) {
|
Log the server's diagnostics if main channel open fails.
This has been a FIXME in the code for ages, because back when the main
channel was always a pty session or a program run in a pipe, there
weren't that many circumstances in which the actual CHANNEL_OPEN could
return failure, so it never seemed like a priority to get round to
pulling the error information out of the CHANNEL_OPEN_FAILURE response
message and including it in PuTTY or Plink's local error message.
However, 'plink -nc' is the real reason why this is actually
important; if you tell the SSH server to make a direct-tcpip network
connection as its main channel, then that can fail for all the usual
network-unreliability reasons, and you actually do want to know which
(did you misspell the hostname, or is the target server refusing
connections, or has network connectivity failed?). This actually bit
me today when I had such a network failure, and had to debug it by
pulling that information manually out of a packet log. Time to
eliminate that FIXME.
So I've pulled the error-extracting code out of the previous handler
for OPEN_FAILURE on non-main channels into a separate function, and
arranged to call that function if the main channel open fails too. In
the process I've made a couple of minor tweaks, e.g. if the server
sends back a reason code we haven't heard of, we say _what_ that
reason code was, and also we at least make a token effort to spot if
we see a packet other than OPEN_{CONFIRMATION,FAILURE} reaching the
main loop in response to the main channel-open.
2017-06-15 17:58:01 +00:00
|
|
|
bombout(("Server's response to main channel open cited wrong"
|
|
|
|
" channel number"));
|
|
|
|
crStopV;
|
|
|
|
}
|
|
|
|
if (pktin->type == SSH2_MSG_CHANNEL_OPEN_FAILURE) {
|
|
|
|
char *errtext = ssh2_channel_open_failure_error_text(pktin);
|
|
|
|
bombout(("Server refused to open main channel: %s", errtext));
|
|
|
|
sfree(errtext);
|
2004-10-13 13:43:11 +00:00
|
|
|
crStopV;
|
|
|
|
}
|
Log the server's diagnostics if main channel open fails.
This has been a FIXME in the code for ages, because back when the main
channel was always a pty session or a program run in a pipe, there
weren't that many circumstances in which the actual CHANNEL_OPEN could
return failure, so it never seemed like a priority to get round to
pulling the error information out of the CHANNEL_OPEN_FAILURE response
message and including it in PuTTY or Plink's local error message.
However, 'plink -nc' is the real reason why this is actually
important; if you tell the SSH server to make a direct-tcpip network
connection as its main channel, then that can fail for all the usual
network-unreliability reasons, and you actually do want to know which
(did you misspell the hostname, or is the target server refusing
connections, or has network connectivity failed?). This actually bit
me today when I had such a network failure, and had to debug it by
pulling that information manually out of a packet log. Time to
eliminate that FIXME.
So I've pulled the error-extracting code out of the previous handler
for OPEN_FAILURE on non-main channels into a separate function, and
arranged to call that function if the main channel open fails too. In
the process I've made a couple of minor tweaks, e.g. if the server
sends back a reason code we haven't heard of, we say _what_ that
reason code was, and also we at least make a token effort to spot if
we see a packet other than OPEN_{CONFIRMATION,FAILURE} reaching the
main loop in response to the main channel-open.
2017-06-15 17:58:01 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
ssh->mainchan->remoteid = get_uint32(pktin);
|
2005-01-22 16:06:21 +00:00
|
|
|
ssh->mainchan->halfopen = FALSE;
|
2018-05-27 17:13:53 +00:00
|
|
|
ssh->mainchan->v.v2.remwindow = get_uint32(pktin);
|
|
|
|
ssh->mainchan->v.v2.remmaxpkt = get_uint32(pktin);
|
2004-10-17 15:32:42 +00:00
|
|
|
update_specials_menu(ssh->frontend);
|
2012-09-08 19:46:07 +00:00
|
|
|
logevent("Opened main channel");
|
2006-08-28 15:12:37 +00:00
|
|
|
}
|
2000-09-07 16:33:49 +00:00
|
|
|
|
2004-11-27 15:32:45 +00:00
|
|
|
/*
|
|
|
|
* Now we have a channel, make dispatch table entries for
|
|
|
|
* general channel-based messages.
|
|
|
|
*/
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] =
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] =
|
|
|
|
ssh2_msg_channel_data;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = ssh2_msg_channel_eof;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = ssh2_msg_channel_close;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] =
|
|
|
|
ssh2_msg_channel_open_confirmation;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] =
|
|
|
|
ssh2_msg_channel_open_failure;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_REQUEST] =
|
|
|
|
ssh2_msg_channel_request;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] =
|
|
|
|
ssh2_msg_channel_open;
|
2012-08-25 13:39:32 +00:00
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_channel_response;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_response;
|
|
|
|
|
2018-05-25 20:17:09 +00:00
|
|
|
/*
|
|
|
|
* Put our current pending packet queue back to the front of
|
|
|
|
* pq_full, and then schedule a callback to re-process those
|
|
|
|
* packets (if any). That way, anything already in our queue that
|
|
|
|
* matches any of the table entries we've just modified will go to
|
|
|
|
* the right handler function, and won't come here to confuse us.
|
|
|
|
*/
|
2018-09-19 19:30:40 +00:00
|
|
|
if (pq_peek(&ssh->pq_ssh2_connection)) {
|
|
|
|
pq_concatenate(&ssh->pq_full, &ssh->pq_ssh2_connection, &ssh->pq_full);
|
2018-05-25 20:17:09 +00:00
|
|
|
queue_idempotent_callback(&ssh->pq_full_consumer);
|
2018-09-19 19:30:40 +00:00
|
|
|
}
|
2018-05-25 20:17:09 +00:00
|
|
|
|
2013-11-17 14:05:41 +00:00
|
|
|
/*
|
|
|
|
* Now the connection protocol is properly up and running, with
|
|
|
|
* all those dispatch table entries, so it's safe to let
|
|
|
|
* downstreams start trying to open extra channels through us.
|
|
|
|
*/
|
|
|
|
if (ssh->connshare)
|
|
|
|
share_activate(ssh->connshare, ssh->v_s);
|
2004-11-27 15:32:45 +00:00
|
|
|
|
2013-11-17 14:05:41 +00:00
|
|
|
if (ssh->mainchan && ssh_is_simple(ssh)) {
|
2007-08-05 14:18:43 +00:00
|
|
|
/*
|
|
|
|
* This message indicates to the server that we promise
|
|
|
|
* not to try to run any other channel in parallel with
|
|
|
|
* this one, so it's safe for it to advertise a very large
|
|
|
|
* window and leave the flow control to TCP.
|
|
|
|
*/
|
2012-08-25 15:57:05 +00:00
|
|
|
s->pktout = ssh2_chanreq_init(ssh->mainchan,
|
|
|
|
"simple@putty.projects.tartarus.org",
|
|
|
|
NULL, NULL);
|
2007-08-05 14:18:43 +00:00
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
|
|
|
}
|
|
|
|
|
2012-08-02 22:18:18 +00:00
|
|
|
/*
|
|
|
|
* Enable port forwardings.
|
|
|
|
*/
|
2018-09-14 16:04:39 +00:00
|
|
|
portfwdmgr_config(ssh->portfwdmgr, ssh->conf);
|
2012-08-02 22:18:18 +00:00
|
|
|
|
2012-09-01 12:03:12 +00:00
|
|
|
if (ssh->mainchan && !ssh->ncmode) {
|
|
|
|
/*
|
|
|
|
* Send the CHANNEL_REQUESTS for the main session channel.
|
|
|
|
* Each one is handled by its own little asynchronous
|
|
|
|
* co-routine.
|
|
|
|
*/
|
2012-08-02 22:18:18 +00:00
|
|
|
|
2012-09-01 12:03:12 +00:00
|
|
|
/* Potentially enable X11 forwarding. */
|
2013-11-17 14:05:10 +00:00
|
|
|
if (conf_get_int(ssh->conf, CONF_x11_forward)) {
|
|
|
|
ssh->x11disp =
|
|
|
|
x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display),
|
|
|
|
ssh->conf);
|
|
|
|
if (!ssh->x11disp) {
|
|
|
|
/* FIXME: return an error message from x11_setup_display */
|
|
|
|
logevent("X11 forwarding not enabled: unable to"
|
|
|
|
" initialise X display");
|
|
|
|
} else {
|
|
|
|
ssh->x11auth = x11_invent_fake_auth
|
|
|
|
(ssh->x11authtree, conf_get_int(ssh->conf, CONF_x11_auth));
|
|
|
|
ssh->x11auth->disp = ssh->x11disp;
|
|
|
|
|
|
|
|
ssh2_setup_x11(ssh->mainchan, NULL, NULL);
|
|
|
|
}
|
|
|
|
}
|
2001-08-09 21:17:05 +00:00
|
|
|
|
2012-09-01 12:03:12 +00:00
|
|
|
/* Potentially enable agent forwarding. */
|
2018-09-17 11:14:00 +00:00
|
|
|
if (ssh_agent_forwarding_permitted(&ssh->cl))
|
2012-09-01 12:03:12 +00:00
|
|
|
ssh2_setup_agent(ssh->mainchan, NULL, NULL);
|
2001-03-03 16:38:44 +00:00
|
|
|
|
2012-09-01 12:03:12 +00:00
|
|
|
/* Now allocate a pty for the session. */
|
|
|
|
if (!conf_get_int(ssh->conf, CONF_nopty))
|
|
|
|
ssh2_setup_pty(ssh->mainchan, NULL, NULL);
|
2000-09-07 16:33:49 +00:00
|
|
|
|
2012-09-01 12:03:12 +00:00
|
|
|
/* Send environment variables. */
|
|
|
|
ssh2_setup_env(ssh->mainchan, NULL, NULL);
|
2004-10-16 10:56:54 +00:00
|
|
|
|
2012-09-01 12:03:12 +00:00
|
|
|
/*
|
|
|
|
* Start a shell or a remote command. We may have to attempt
|
|
|
|
* this twice if the config data has provided a second choice
|
|
|
|
* of command.
|
|
|
|
*/
|
|
|
|
while (1) {
|
|
|
|
int subsys;
|
|
|
|
char *cmd;
|
2001-08-26 18:32:28 +00:00
|
|
|
|
2012-09-01 12:03:12 +00:00
|
|
|
if (ssh->fallback_cmd) {
|
|
|
|
subsys = conf_get_int(ssh->conf, CONF_ssh_subsys2);
|
|
|
|
cmd = conf_get_str(ssh->conf, CONF_remote_cmd2);
|
|
|
|
} else {
|
|
|
|
subsys = conf_get_int(ssh->conf, CONF_ssh_subsys);
|
|
|
|
cmd = conf_get_str(ssh->conf, CONF_remote_cmd);
|
|
|
|
}
|
2001-08-26 18:32:28 +00:00
|
|
|
|
2012-09-01 12:03:12 +00:00
|
|
|
if (subsys) {
|
|
|
|
s->pktout = ssh2_chanreq_init(ssh->mainchan, "subsystem",
|
2018-05-18 06:22:59 +00:00
|
|
|
ssh2_response_connection, NULL);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, cmd);
|
2012-09-01 12:03:12 +00:00
|
|
|
} else if (*cmd) {
|
|
|
|
s->pktout = ssh2_chanreq_init(ssh->mainchan, "exec",
|
2018-05-18 06:22:59 +00:00
|
|
|
ssh2_response_connection, NULL);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(s->pktout, cmd);
|
2012-09-01 12:03:12 +00:00
|
|
|
} else {
|
|
|
|
s->pktout = ssh2_chanreq_init(ssh->mainchan, "shell",
|
2018-05-18 06:22:59 +00:00
|
|
|
ssh2_response_connection, NULL);
|
2012-09-01 12:03:12 +00:00
|
|
|
}
|
|
|
|
ssh2_pkt_send(ssh, s->pktout);
|
2004-11-24 20:35:15 +00:00
|
|
|
|
2018-05-18 06:22:59 +00:00
|
|
|
crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh2_connection)) != NULL);
|
2004-11-24 20:35:15 +00:00
|
|
|
|
2012-09-01 12:03:12 +00:00
|
|
|
if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {
|
|
|
|
if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {
|
|
|
|
bombout(("Unexpected response to shell/command request:"
|
|
|
|
" packet type %d", pktin->type));
|
|
|
|
crStopV;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* We failed to start the command. If this is the
|
|
|
|
* fallback command, we really are finished; if it's
|
|
|
|
* not, and if the fallback command exists, try falling
|
|
|
|
* back to it before complaining.
|
|
|
|
*/
|
|
|
|
if (!ssh->fallback_cmd &&
|
|
|
|
*conf_get_str(ssh->conf, CONF_remote_cmd2)) {
|
|
|
|
logevent("Primary command failed; attempting fallback");
|
|
|
|
ssh->fallback_cmd = TRUE;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
bombout(("Server refused to start a shell/command"));
|
2003-04-23 13:48:09 +00:00
|
|
|
crStopV;
|
2012-09-01 12:03:12 +00:00
|
|
|
} else {
|
|
|
|
logevent("Started a shell/command");
|
2001-08-26 18:32:28 +00:00
|
|
|
}
|
2012-09-01 12:03:12 +00:00
|
|
|
break;
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
2012-09-01 12:03:12 +00:00
|
|
|
} else {
|
|
|
|
ssh->editing = ssh->echoing = TRUE;
|
2000-09-07 16:33:49 +00:00
|
|
|
}
|
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->state = SSH_STATE_SESSION;
|
|
|
|
if (ssh->size_needed)
|
2018-09-11 15:23:38 +00:00
|
|
|
backend_size(&ssh->backend, ssh->term_width, ssh->term_height);
|
2002-10-25 11:30:33 +00:00
|
|
|
if (ssh->eof_needed)
|
2018-09-11 15:23:38 +00:00
|
|
|
backend_special(&ssh->backend, TS_EOF);
|
2000-10-24 09:49:03 +00:00
|
|
|
|
2000-09-07 16:33:49 +00:00
|
|
|
/*
|
|
|
|
* Transfer data!
|
|
|
|
*/
|
2002-10-26 10:16:19 +00:00
|
|
|
if (ssh->ldisc)
|
Move echo/edit state change functionality out of ldisc_send.
I'm not actually sure why we've always had back ends notify ldisc of
changes to echo/edit settings by giving ldisc_send(ldisc,NULL,0,0) a
special meaning, instead of by having a separate dedicated notify
function with its own prototype and parameter set. Coverity's recent
observation that the two kinds of call don't even have the same
requirements on the ldisc (particularly, whether ldisc->term can be
NULL) makes me realise that it's really high time I separated the two
conceptually different operations into actually different functions.
While I'm here, I've renamed the confusing ldisc_update() function
which that special operation ends up feeding to, because it's not
actually a function applying to an ldisc - it applies to a front end.
So ldisc_send(ldisc,NULL,0,0) is now ldisc_echoedit_update(ldisc), and
that in turn figures out the current echo/edit settings before passing
them on to frontend_echoedit_update(). I think that should be clearer.
2014-11-22 16:12:47 +00:00
|
|
|
ldisc_echoedit_update(ssh->ldisc); /* cause ldisc to notice changes */
|
2004-10-13 13:43:11 +00:00
|
|
|
if (ssh->mainchan)
|
|
|
|
ssh->send_ok = 1;
|
2000-09-07 16:33:49 +00:00
|
|
|
while (1) {
|
2018-05-18 06:22:59 +00:00
|
|
|
if ((pktin = pq_pop(&ssh->pq_ssh2_connection)) != NULL) {
|
2004-10-18 00:41:48 +00:00
|
|
|
|
2004-11-27 15:32:45 +00:00
|
|
|
/*
|
|
|
|
* _All_ the connection-layer packets we expect to
|
|
|
|
* receive are now handled by the dispatch table.
|
|
|
|
* Anything that reaches here must be bogus.
|
|
|
|
*/
|
2001-05-06 14:35:20 +00:00
|
|
|
|
2004-11-27 15:32:45 +00:00
|
|
|
bombout(("Strange packet received: type %d", pktin->type));
|
|
|
|
crStopV;
|
2018-05-18 06:22:58 +00:00
|
|
|
}
|
|
|
|
while (ssh->mainchan && bufchain_size(&ssh->user_input) > 0) {
|
2001-05-06 14:35:20 +00:00
|
|
|
/*
|
2018-05-18 06:22:58 +00:00
|
|
|
* Add user input to the main channel's buffer.
|
2001-05-06 14:35:20 +00:00
|
|
|
*/
|
2018-05-18 06:22:58 +00:00
|
|
|
void *data;
|
|
|
|
int len;
|
|
|
|
bufchain_prefix(&ssh->user_input, &data, &len);
|
|
|
|
ssh_send_channel_data(ssh->mainchan, data, len);
|
|
|
|
bufchain_consume(&ssh->user_input, len);
|
2000-09-07 16:33:49 +00:00
|
|
|
}
|
Restructure some loops with crReturn in them.
A large part of the purpose of the 20-patch series just past is to
arrange that user input is never dropped on the floor: if you type it
at a moment where the active protocol coroutine has nothing it can
usefully do with it, the default action will now be to leave it on the
user_input queue where it will eventually be picked up by some later
coroutine, or later phase of this one, that does.
And did I _test_ this feature, end to end, just once, before pushing
the giant patch series?
I did not.
It doesn't work, and the reason why it doesn't work is because various
loops that spin round alternating a crReturn with a check of the input
queue do the first crReturn _before_ the first queue check. So if
there's already data in the queue, well, it won't be _dropped_, but it
also won't be passed on immediately to where it needs to be - instead,
it will sit in the queue until you press _another_ key, at which point
a queue check will happen and all your backed-up typeahead data will
come out.
Fixed by restructuring those loops to do a queue check first. This
applies to the final loop in do_ssh2_connection, and all the little
loops during userauth that prompt for usernames, passwords,
passphrases etc via get_userpass_input.
2018-05-18 10:01:12 +00:00
|
|
|
crReturnV;
|
2000-09-05 14:28:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
crFinishV;
|
|
|
|
}
|
|
|
|
|
2018-05-18 06:22:59 +00:00
|
|
|
static void ssh2_connection_input(Ssh ssh)
|
|
|
|
{
|
|
|
|
do_ssh2_connection(ssh);
|
|
|
|
}
|
|
|
|
|
2004-11-24 20:35:15 +00:00
|
|
|
/*
|
2005-03-10 16:36:05 +00:00
|
|
|
* Handlers for SSH-2 messages that might arrive at any moment.
|
2004-11-24 20:35:15 +00:00
|
|
|
*/
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_disconnect(Ssh ssh, PktIn *pktin)
|
2004-11-24 20:35:15 +00:00
|
|
|
{
|
|
|
|
/* log reason code in disconnect message */
|
2018-05-27 17:13:53 +00:00
|
|
|
char *buf;
|
|
|
|
ptrlen msg;
|
|
|
|
int reason;
|
2004-11-24 20:35:15 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
reason = get_uint32(pktin);
|
|
|
|
msg = get_string(pktin);
|
2004-11-24 20:35:15 +00:00
|
|
|
|
|
|
|
if (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) {
|
|
|
|
buf = dupprintf("Received disconnect message (%s)",
|
|
|
|
ssh2_disconnect_reasons[reason]);
|
|
|
|
} else {
|
|
|
|
buf = dupprintf("Received disconnect message (unknown"
|
|
|
|
" type %d)", reason);
|
|
|
|
}
|
|
|
|
logevent(buf);
|
|
|
|
sfree(buf);
|
2018-05-27 17:13:53 +00:00
|
|
|
buf = dupprintf("Disconnection message text: %.*s", PTRLEN_PRINTF(msg));
|
2004-11-24 20:35:15 +00:00
|
|
|
logevent(buf);
|
2008-05-31 17:22:29 +00:00
|
|
|
bombout(("Server sent disconnect message\ntype %d (%s):\n\"%.*s\"",
|
2004-11-24 20:35:15 +00:00
|
|
|
reason,
|
|
|
|
(reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ?
|
2018-05-27 17:13:53 +00:00
|
|
|
ssh2_disconnect_reasons[reason] : "unknown", PTRLEN_PRINTF(msg)));
|
2004-11-24 20:35:15 +00:00
|
|
|
sfree(buf);
|
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_debug(Ssh ssh, PktIn *pktin)
|
2004-11-24 20:35:15 +00:00
|
|
|
{
|
|
|
|
/* log the debug message */
|
2018-05-27 17:13:53 +00:00
|
|
|
ptrlen msg;
|
2004-11-24 20:35:15 +00:00
|
|
|
|
2011-05-07 10:57:19 +00:00
|
|
|
/* XXX maybe we should actually take notice of the return value */
|
2018-05-27 17:13:53 +00:00
|
|
|
get_bool(pktin);
|
|
|
|
msg = get_string(pktin);
|
2004-11-24 20:35:15 +00:00
|
|
|
|
2018-05-27 17:13:53 +00:00
|
|
|
logeventf(ssh, "Remote debug message: %.*s", PTRLEN_PRINTF(msg));
|
2004-11-24 20:35:15 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_transport(Ssh ssh, PktIn *pktin)
|
2012-08-21 22:04:56 +00:00
|
|
|
{
|
2018-05-18 06:22:58 +00:00
|
|
|
pktin->refcount++; /* avoid packet being freed when we return */
|
|
|
|
pq_push(&ssh->pq_ssh2_transport, pktin);
|
|
|
|
queue_idempotent_callback(&ssh->ssh2_transport_icb);
|
2012-08-21 22:04:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called if we receive a packet that isn't allowed by the protocol.
|
|
|
|
* This only applies to packets whose meaning PuTTY understands.
|
|
|
|
* Entirely unknown packets are handled below.
|
|
|
|
*/
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_unexpected(Ssh ssh, PktIn *pktin)
|
2012-08-21 22:04:56 +00:00
|
|
|
{
|
|
|
|
char *buf = dupprintf("Server protocol violation: unexpected %s packet",
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh2_pkt_type(ssh->pls.kctx, ssh->pls.actx,
|
2012-08-21 22:04:56 +00:00
|
|
|
pktin->type));
|
|
|
|
ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
|
2012-08-21 23:04:22 +00:00
|
|
|
sfree(buf);
|
2012-08-21 22:04:56 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_msg_something_unimplemented(Ssh ssh, PktIn *pktin)
|
2004-11-24 20:35:15 +00:00
|
|
|
{
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_UNIMPLEMENTED);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(pktout, pktin->sequence);
|
2004-11-24 20:35:15 +00:00
|
|
|
/*
|
|
|
|
* UNIMPLEMENTED messages MUST appear in the same order as the
|
|
|
|
* messages they respond to. Hence, never queue them.
|
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh_pkt_write(ssh, pktout);
|
2004-11-24 20:35:15 +00:00
|
|
|
}
|
|
|
|
|
2000-09-07 16:33:49 +00:00
|
|
|
/*
|
2005-03-10 16:36:05 +00:00
|
|
|
* Handle the top-level SSH-2 protocol.
|
2000-09-07 16:33:49 +00:00
|
|
|
*/
|
2004-11-24 20:35:15 +00:00
|
|
|
static void ssh2_protocol_setup(Ssh ssh)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->bpp = ssh2_bpp_new();
|
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
#ifndef NO_GSSAPI
|
|
|
|
/* Load and pick the highest GSS library on the preference list. */
|
|
|
|
if (!ssh->gsslibs)
|
|
|
|
ssh->gsslibs = ssh_gss_setup(ssh->conf);
|
|
|
|
ssh->gsslib = NULL;
|
|
|
|
if (ssh->gsslibs->nlibraries > 0) {
|
|
|
|
int i, j;
|
|
|
|
for (i = 0; i < ngsslibs; i++) {
|
|
|
|
int want_id = conf_get_int_int(ssh->conf,
|
|
|
|
CONF_ssh_gsslist, i);
|
|
|
|
for (j = 0; j < ssh->gsslibs->nlibraries; j++)
|
|
|
|
if (ssh->gsslibs->libraries[j].id == want_id) {
|
|
|
|
ssh->gsslib = &ssh->gsslibs->libraries[j];
|
|
|
|
goto got_gsslib; /* double break */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
got_gsslib:
|
|
|
|
/*
|
|
|
|
* We always expect to have found something in
|
|
|
|
* the above loop: we only came here if there
|
|
|
|
* was at least one viable GSS library, and the
|
|
|
|
* preference list should always mention
|
|
|
|
* everything and only change the order.
|
|
|
|
*/
|
|
|
|
assert(ssh->gsslib);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-11-24 20:35:15 +00:00
|
|
|
/*
|
|
|
|
* Most messages cause SSH2_MSG_UNIMPLEMENTED.
|
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
for (i = 0; i < SSH_MAX_MSG; i++)
|
2004-11-24 20:35:15 +00:00
|
|
|
ssh->packet_dispatch[i] = ssh2_msg_something_unimplemented;
|
|
|
|
|
|
|
|
/*
|
2012-08-21 22:04:56 +00:00
|
|
|
* Initially, we only accept transport messages (and a few generic
|
2018-05-18 06:22:59 +00:00
|
|
|
* ones). do_ssh2_userauth and do_ssh2_connection will each add
|
|
|
|
* more when they start. Messages that are understood but not
|
|
|
|
* currently acceptable go to ssh2_msg_unexpected.
|
2004-11-24 20:35:15 +00:00
|
|
|
*/
|
2012-08-21 22:04:56 +00:00
|
|
|
ssh->packet_dispatch[SSH2_MSG_UNIMPLEMENTED] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_SERVICE_REQUEST] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_SERVICE_ACCEPT] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_KEXINIT] = ssh2_msg_transport;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_NEWKEYS] = ssh2_msg_transport;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_KEXDH_INIT] = ssh2_msg_transport;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_KEXDH_REPLY] = ssh2_msg_transport;
|
|
|
|
/* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REQUEST] = ssh2_msg_transport; duplicate case value */
|
|
|
|
/* ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_GROUP] = ssh2_msg_transport; duplicate case value */
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_INIT] = ssh2_msg_transport;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_KEX_DH_GEX_REPLY] = ssh2_msg_transport;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
ssh->packet_dispatch[SSH2_MSG_KEXGSS_GROUP] = ssh2_msg_transport;
|
2012-08-21 22:04:56 +00:00
|
|
|
ssh->packet_dispatch[SSH2_MSG_USERAUTH_REQUEST] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_USERAUTH_FAILURE] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_USERAUTH_SUCCESS] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_USERAUTH_BANNER] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_USERAUTH_PK_OK] = ssh2_msg_unexpected;
|
|
|
|
/* ssh->packet_dispatch[SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ] = ssh2_msg_unexpected; duplicate case value */
|
|
|
|
/* ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_REQUEST] = ssh2_msg_unexpected; duplicate case value */
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_USERAUTH_INFO_RESPONSE] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_REQUEST_SUCCESS] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_REQUEST_FAILURE] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_REQUEST] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_unexpected;
|
2004-11-24 20:35:15 +00:00
|
|
|
|
|
|
|
/*
|
2012-08-21 22:04:56 +00:00
|
|
|
* These messages have a special handler from the start.
|
2004-11-24 20:35:15 +00:00
|
|
|
*/
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_DISCONNECT] = ssh2_msg_disconnect;
|
2005-03-10 16:36:05 +00:00
|
|
|
ssh->packet_dispatch[SSH2_MSG_IGNORE] = ssh_msg_ignore; /* shared with SSH-1 */
|
2004-11-24 20:35:15 +00:00
|
|
|
ssh->packet_dispatch[SSH2_MSG_DEBUG] = ssh2_msg_debug;
|
|
|
|
}
|
|
|
|
|
2013-11-17 14:05:41 +00:00
|
|
|
static void ssh2_bare_connection_protocol_setup(Ssh ssh)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->bpp = ssh2_bare_bpp_new();
|
|
|
|
|
2013-11-17 14:05:41 +00:00
|
|
|
/*
|
|
|
|
* Most messages cause SSH2_MSG_UNIMPLEMENTED.
|
|
|
|
*/
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
for (i = 0; i < SSH_MAX_MSG; i++)
|
2013-11-17 14:05:41 +00:00
|
|
|
ssh->packet_dispatch[i] = ssh2_msg_something_unimplemented;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initially, we set all ssh-connection messages to 'unexpected';
|
2018-05-18 06:22:59 +00:00
|
|
|
* do_ssh2_connection will fill things in properly. We also handle
|
|
|
|
* a couple of messages from the transport protocol which aren't
|
2013-11-17 14:05:41 +00:00
|
|
|
* related to key exchange (UNIMPLEMENTED, IGNORE, DEBUG,
|
|
|
|
* DISCONNECT).
|
|
|
|
*/
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_GLOBAL_REQUEST] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_REQUEST_SUCCESS] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_REQUEST_FAILURE] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_CONFIRMATION] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_OPEN_FAILURE] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_WINDOW_ADJUST] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_DATA] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_EXTENDED_DATA] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_EOF] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_CLOSE] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_REQUEST] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_unexpected;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_unexpected;
|
|
|
|
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_UNIMPLEMENTED] = ssh2_msg_unexpected;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* These messages have a special handler from the start.
|
|
|
|
*/
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_DISCONNECT] = ssh2_msg_disconnect;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_IGNORE] = ssh_msg_ignore;
|
|
|
|
ssh->packet_dispatch[SSH2_MSG_DEBUG] = ssh2_msg_debug;
|
|
|
|
}
|
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
#ifndef NO_GSSAPI
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static PktOut *ssh2_gss_authpacket(Ssh ssh, Ssh_gss_ctx gss_ctx,
|
|
|
|
const char *authtype)
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
{
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
strbuf *sb;
|
|
|
|
PktOut *p;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
Ssh_gss_buf buf;
|
|
|
|
Ssh_gss_buf mic;
|
|
|
|
|
|
|
|
/*
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
* The mic is computed over the session id + intended
|
|
|
|
* USERAUTH_REQUEST packet.
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
*/
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
sb = strbuf_new();
|
|
|
|
put_string(sb, ssh->v2_session_id, ssh->v2_session_id_len);
|
|
|
|
put_byte(sb, SSH2_MSG_USERAUTH_REQUEST);
|
|
|
|
put_stringz(sb, ssh->username);
|
|
|
|
put_stringz(sb, "ssh-connection");
|
|
|
|
put_stringz(sb, authtype);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
|
|
|
|
/* Compute the mic */
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
buf.value = sb->s;
|
|
|
|
buf.length = sb->len;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
ssh->gsslib->get_mic(ssh->gsslib, gss_ctx, &buf, &mic);
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
strbuf_free(sb);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
|
|
|
|
/* Now we can build the real packet */
|
|
|
|
if (strcmp(authtype, "gssapi-with-mic") == 0) {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
p = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_USERAUTH_GSSAPI_MIC);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
} else {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
p = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_USERAUTH_REQUEST);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(p, ssh->username);
|
|
|
|
put_stringz(p, "ssh-connection");
|
|
|
|
put_stringz(p, authtype);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
}
|
2018-05-24 09:15:36 +00:00
|
|
|
put_string(p, mic.value, mic.length);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is called at the beginning of each SSH rekey to determine whether we are
|
|
|
|
* GSS capable, and if we did GSS key exchange, and are delegating credentials,
|
|
|
|
* it is also called periodically to determine whether we should rekey in order
|
|
|
|
* to delegate (more) fresh credentials. This is called "credential cascading".
|
|
|
|
*
|
|
|
|
* On Windows, with SSPI, we may not get the credential expiration, as Windows
|
|
|
|
* automatically renews from cached passwords, so the credential effectively
|
|
|
|
* never expires. Since we still want to cascade when the local TGT is updated,
|
|
|
|
* we use the expiration of a newly obtained context as a proxy for the
|
|
|
|
* expiration of the TGT.
|
|
|
|
*/
|
2018-05-01 17:54:04 +00:00
|
|
|
static void ssh2_gss_update(Ssh ssh, int definitely_rekeying)
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
{
|
|
|
|
int gss_stat;
|
|
|
|
time_t gss_cred_expiry;
|
|
|
|
unsigned long mins;
|
|
|
|
Ssh_gss_buf gss_sndtok;
|
|
|
|
Ssh_gss_buf gss_rcvtok;
|
|
|
|
Ssh_gss_ctx gss_ctx;
|
|
|
|
|
|
|
|
ssh->gss_status = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Nothing to do if no GSSAPI libraries are configured or GSSAPI auth is not
|
|
|
|
* enabled.
|
|
|
|
*/
|
|
|
|
if (ssh->gsslibs->nlibraries == 0)
|
|
|
|
return;
|
2018-04-26 18:15:15 +00:00
|
|
|
if (!conf_get_int(ssh->conf, CONF_try_gssapi_auth) &&
|
|
|
|
!conf_get_int(ssh->conf, CONF_try_gssapi_kex))
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* Import server name and cache it */
|
|
|
|
if (ssh->gss_srv_name == GSS_C_NO_NAME) {
|
|
|
|
gss_stat = ssh->gsslib->import_name(ssh->gsslib,
|
|
|
|
ssh->fullhostname,
|
|
|
|
&ssh->gss_srv_name);
|
|
|
|
if (gss_stat != SSH_GSS_OK) {
|
|
|
|
if (gss_stat == SSH_GSS_BAD_HOST_NAME)
|
|
|
|
logevent("GSSAPI import name failed -"
|
|
|
|
" Bad service name; won't use GSS key exchange");
|
|
|
|
else
|
|
|
|
logevent("GSSAPI import name failed;"
|
|
|
|
" won't use GSS key exchange");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do we (still) have credentials? Capture the credential expiration when
|
|
|
|
* available
|
|
|
|
*/
|
|
|
|
gss_stat = ssh->gsslib->acquire_cred(ssh->gsslib,
|
|
|
|
&gss_ctx,
|
|
|
|
&gss_cred_expiry);
|
|
|
|
if (gss_stat != SSH_GSS_OK)
|
|
|
|
return;
|
|
|
|
|
|
|
|
SSH_GSS_CLEAR_BUF(&gss_sndtok);
|
|
|
|
SSH_GSS_CLEAR_BUF(&gss_rcvtok);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When acquire_cred yields no useful expiration, get a proxy for the cred
|
|
|
|
* expiration from the context expiration.
|
|
|
|
*/
|
|
|
|
gss_stat = ssh->gsslib->init_sec_context(
|
|
|
|
ssh->gsslib, &gss_ctx, ssh->gss_srv_name,
|
|
|
|
0 /* don't delegate */, &gss_rcvtok, &gss_sndtok,
|
|
|
|
(gss_cred_expiry == GSS_NO_EXPIRATION ? &gss_cred_expiry : NULL),
|
|
|
|
&ssh->gss_ctxt_lifetime);
|
|
|
|
|
|
|
|
/* This context was for testing only. */
|
|
|
|
if (gss_ctx)
|
|
|
|
ssh->gsslib->release_cred(ssh->gsslib, &gss_ctx);
|
|
|
|
|
|
|
|
if (gss_stat != SSH_GSS_OK &&
|
|
|
|
gss_stat != SSH_GSS_S_CONTINUE_NEEDED) {
|
2018-05-01 17:54:04 +00:00
|
|
|
/*
|
|
|
|
* No point in verbosely interrupting the user to tell them we
|
|
|
|
* couldn't get GSS credentials, if this was only a check
|
|
|
|
* between key exchanges to see if fresh ones were available.
|
|
|
|
* When we do do a rekey, this message (if displayed) will
|
|
|
|
* appear among the standard rekey blurb, but when we're not,
|
|
|
|
* it shouldn't pop up all the time regardless.
|
|
|
|
*/
|
|
|
|
if (definitely_rekeying)
|
|
|
|
logeventf(ssh, "No GSSAPI security context available");
|
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gss_sndtok.length)
|
|
|
|
ssh->gsslib->free_tok(ssh->gsslib, &gss_sndtok);
|
|
|
|
|
|
|
|
ssh->gss_status |= GSS_KEX_CAPABLE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When rekeying to cascade, avoding doing this too close to the context
|
|
|
|
* expiration time, since the key exchange might fail.
|
|
|
|
*/
|
|
|
|
if (ssh->gss_ctxt_lifetime < MIN_CTXT_LIFETIME)
|
|
|
|
ssh->gss_status |= GSS_CTXT_MAYFAIL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we're not delegating credentials, rekeying is not used to refresh
|
|
|
|
* them. We must avoid setting GSS_CRED_UPDATED or GSS_CTXT_EXPIRES when
|
|
|
|
* credential delegation is disabled.
|
|
|
|
*/
|
|
|
|
if (conf_get_int(ssh->conf, CONF_gssapifwd) == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ssh->gss_cred_expiry != GSS_NO_EXPIRATION &&
|
|
|
|
difftime(gss_cred_expiry, ssh->gss_cred_expiry) > 0)
|
|
|
|
ssh->gss_status |= GSS_CRED_UPDATED;
|
|
|
|
|
|
|
|
mins = conf_get_int(ssh->conf, CONF_gssapirekey);
|
|
|
|
mins = rekey_mins(mins, GSS_DEF_REKEY_MINS);
|
|
|
|
if (mins > 0 && ssh->gss_ctxt_lifetime <= mins * 60)
|
|
|
|
ssh->gss_status |= GSS_CTXT_EXPIRES;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-04-14 13:48:02 +00:00
|
|
|
/*
|
|
|
|
* The rekey_time is zero except when re-configuring.
|
|
|
|
*
|
|
|
|
* We either schedule the next timer and return 0, or return 1 to run the
|
|
|
|
* callback now, which will call us again to re-schedule on completion.
|
|
|
|
*/
|
|
|
|
static int ssh2_timer_update(Ssh ssh, unsigned long rekey_time)
|
|
|
|
{
|
|
|
|
unsigned long mins;
|
|
|
|
unsigned long ticks;
|
|
|
|
|
|
|
|
mins = conf_get_int(ssh->conf, CONF_ssh_rekey_time);
|
|
|
|
mins = rekey_mins(mins, 60);
|
|
|
|
ticks = mins * 60 * TICKSPERSEC;
|
|
|
|
|
|
|
|
/* Handle change from previous setting */
|
|
|
|
if (rekey_time != 0 && rekey_time != mins) {
|
|
|
|
unsigned long next;
|
|
|
|
unsigned long now = GETTICKCOUNT();
|
|
|
|
|
|
|
|
mins = rekey_time;
|
|
|
|
ticks = mins * 60 * TICKSPERSEC;
|
|
|
|
next = ssh->last_rekey + ticks;
|
|
|
|
|
|
|
|
/* If overdue, caller will rekey synchronously now */
|
|
|
|
if (now - ssh->last_rekey > ticks)
|
|
|
|
return 1;
|
|
|
|
ticks = next - now;
|
|
|
|
}
|
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
#ifndef NO_GSSAPI
|
2018-05-01 17:54:09 +00:00
|
|
|
if (ssh->gss_kex_used) {
|
|
|
|
/*
|
|
|
|
* If we've used GSSAPI key exchange, then we should
|
|
|
|
* periodically check whether we need to do another one to
|
|
|
|
* pass new credentials to the server.
|
|
|
|
*/
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
unsigned long gssmins;
|
|
|
|
|
|
|
|
/* Check cascade conditions more frequently if configured */
|
|
|
|
gssmins = conf_get_int(ssh->conf, CONF_gssapirekey);
|
|
|
|
gssmins = rekey_mins(gssmins, GSS_DEF_REKEY_MINS);
|
|
|
|
if (gssmins > 0) {
|
|
|
|
if (gssmins < mins)
|
|
|
|
ticks = (mins = gssmins) * 60 * TICKSPERSEC;
|
|
|
|
|
|
|
|
if ((ssh->gss_status & GSS_KEX_CAPABLE) != 0) {
|
|
|
|
/*
|
|
|
|
* Run next timer even sooner if it would otherwise be too close
|
|
|
|
* to the context expiration time
|
|
|
|
*/
|
|
|
|
if ((ssh->gss_status & GSS_CTXT_EXPIRES) == 0 &&
|
|
|
|
ssh->gss_ctxt_lifetime - mins * 60 < 2 * MIN_CTXT_LIFETIME)
|
|
|
|
ticks -= 2 * MIN_CTXT_LIFETIME * TICKSPERSEC;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-04-14 13:48:02 +00:00
|
|
|
/* Schedule the next timer */
|
|
|
|
ssh->next_rekey = schedule_timer(ticks, ssh2_timer, ssh);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-18 21:42:48 +00:00
|
|
|
static void ssh2_timer(void *ctx, unsigned long now)
|
2004-11-27 14:29:20 +00:00
|
|
|
{
|
|
|
|
Ssh ssh = (Ssh)ctx;
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
unsigned long mins;
|
|
|
|
unsigned long ticks;
|
2004-11-27 14:29:20 +00:00
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
if (ssh->state == SSH_STATE_CLOSED ||
|
|
|
|
ssh->kex_in_progress ||
|
|
|
|
ssh->bare_connection ||
|
|
|
|
now != ssh->next_rekey)
|
2005-03-30 08:27:54 +00:00
|
|
|
return;
|
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
mins = conf_get_int(ssh->conf, CONF_ssh_rekey_time);
|
|
|
|
mins = rekey_mins(mins, 60);
|
|
|
|
if (mins == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Rekey if enough time has elapsed */
|
|
|
|
ticks = mins * 60 * TICKSPERSEC;
|
|
|
|
if (now - ssh->last_rekey > ticks - 30*TICKSPERSEC) {
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->rekey_reason = "timeout";
|
|
|
|
ssh->rekey_class = RK_NORMAL;
|
2018-05-18 06:22:58 +00:00
|
|
|
queue_idempotent_callback(&ssh->ssh2_transport_icb);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef NO_GSSAPI
|
|
|
|
/*
|
|
|
|
* Rekey now if we have a new cred or context expires this cycle, but not if
|
|
|
|
* this is unsafe.
|
|
|
|
*/
|
|
|
|
if (conf_get_int(ssh->conf, CONF_gssapirekey)) {
|
2018-05-01 17:54:04 +00:00
|
|
|
ssh2_gss_update(ssh, FALSE);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
if ((ssh->gss_status & GSS_KEX_CAPABLE) != 0 &&
|
|
|
|
(ssh->gss_status & GSS_CTXT_MAYFAIL) == 0 &&
|
|
|
|
(ssh->gss_status & (GSS_CRED_UPDATED|GSS_CTXT_EXPIRES)) != 0) {
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->rekey_reason = "GSS credentials updated";
|
|
|
|
ssh->rekey_class = RK_GSS_UPDATE;
|
2018-05-18 06:22:58 +00:00
|
|
|
queue_idempotent_callback(&ssh->ssh2_transport_icb);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
return;
|
|
|
|
}
|
2004-11-27 14:29:20 +00:00
|
|
|
}
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Try again later. */
|
|
|
|
(void) ssh2_timer_update(ssh, 0);
|
2004-11-27 14:29:20 +00:00
|
|
|
}
|
|
|
|
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
static void ssh2_general_packet_processing(Ssh ssh, PktIn *pktin)
|
2018-05-18 06:22:57 +00:00
|
|
|
{
|
|
|
|
ssh->incoming_data_size += pktin->encrypted_len;
|
|
|
|
if (!ssh->kex_in_progress &&
|
|
|
|
ssh->max_data_size != 0 &&
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->incoming_data_size > ssh->max_data_size) {
|
|
|
|
ssh->rekey_reason = "too much data received";
|
|
|
|
ssh->rekey_class = RK_NORMAL;
|
2018-05-18 06:22:58 +00:00
|
|
|
queue_idempotent_callback(&ssh->ssh2_transport_icb);
|
2018-05-18 06:22:58 +00:00
|
|
|
}
|
2018-05-18 06:22:57 +00:00
|
|
|
}
|
|
|
|
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
static void ssh_cache_conf_values(Ssh ssh)
|
|
|
|
{
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->pls.omit_passwords = conf_get_int(ssh->conf, CONF_logomitpass);
|
|
|
|
ssh->pls.omit_data = conf_get_int(ssh->conf, CONF_logomitdata);
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
}
|
|
|
|
|
1999-01-08 13:02:13 +00:00
|
|
|
/*
|
2000-10-23 10:32:37 +00:00
|
|
|
* Called to set up the connection.
|
1999-01-08 13:02:13 +00:00
|
|
|
*
|
|
|
|
* Returns an error message, or NULL on success.
|
|
|
|
*/
|
2018-09-12 08:10:51 +00:00
|
|
|
static const char *ssh_init(Frontend *frontend, Backend **backend_handle,
|
2015-05-15 10:15:42 +00:00
|
|
|
Conf *conf,
|
|
|
|
const char *host, int port, char **realhost,
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
int nodelay, int keepalive)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2003-05-04 14:18:18 +00:00
|
|
|
const char *p;
|
2002-10-25 11:30:33 +00:00
|
|
|
Ssh ssh;
|
|
|
|
|
2003-03-29 16:14:26 +00:00
|
|
|
ssh = snew(struct ssh_tag);
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
ssh->conf = conf_copy(conf);
|
|
|
|
ssh_cache_conf_values(ssh);
|
2003-04-04 20:21:05 +00:00
|
|
|
ssh->version = 0; /* when not ready yet */
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->s = NULL;
|
|
|
|
ssh->kex = NULL;
|
2018-09-14 07:48:54 +00:00
|
|
|
ssh->dh_ctx = NULL;
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh->hostkey_alg = NULL;
|
2013-08-18 06:48:20 +00:00
|
|
|
ssh->hostkey_str = NULL;
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->exitcode = -1;
|
2005-01-11 19:33:41 +00:00
|
|
|
ssh->close_expected = FALSE;
|
2005-05-21 16:49:27 +00:00
|
|
|
ssh->clean_exit = FALSE;
|
2018-06-03 05:46:28 +00:00
|
|
|
ssh->disconnect_message_seen = FALSE;
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->state = SSH_STATE_PREPACKET;
|
|
|
|
ssh->size_needed = FALSE;
|
|
|
|
ssh->eof_needed = FALSE;
|
2002-10-26 10:16:19 +00:00
|
|
|
ssh->ldisc = NULL;
|
2002-10-26 12:58:13 +00:00
|
|
|
ssh->logctx = NULL;
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->fallback_cmd = 0;
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->pls.kctx = SSH2_PKTCTX_NOKEX;
|
|
|
|
ssh->pls.actx = SSH2_PKTCTX_NOAUTH;
|
2008-11-17 18:38:09 +00:00
|
|
|
ssh->x11disp = NULL;
|
2013-11-17 14:05:10 +00:00
|
|
|
ssh->x11auth = NULL;
|
|
|
|
ssh->x11authtree = newtree234(x11_authcmp);
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh->v2_cbc_ignore_workaround = FALSE;
|
|
|
|
ssh->bpp = NULL;
|
2004-11-24 20:35:15 +00:00
|
|
|
ssh->do_ssh1_connection_crstate = 0;
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->do_ssh_init_state = NULL;
|
2013-11-17 14:05:41 +00:00
|
|
|
ssh->do_ssh_connection_init_state = NULL;
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->do_ssh1_login_state = NULL;
|
|
|
|
ssh->do_ssh2_transport_state = NULL;
|
2018-05-18 06:22:59 +00:00
|
|
|
ssh->do_ssh2_userauth_state = NULL;
|
|
|
|
ssh->do_ssh2_connection_state = NULL;
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
bufchain_init(&ssh->incoming_data);
|
|
|
|
ssh->incoming_data_seen_eof = FALSE;
|
|
|
|
ssh->incoming_data_eof_message = NULL;
|
|
|
|
ssh->incoming_data_consumer.fn = ssh_process_incoming_data;
|
|
|
|
ssh->incoming_data_consumer.ctx = ssh;
|
|
|
|
ssh->incoming_data_consumer.queued = FALSE;
|
2018-09-19 20:56:11 +00:00
|
|
|
pq_in_init(&ssh->pq_full);
|
Make the rdpkt functions output to a PacketQueue.
Each of the coroutines that parses the incoming wire data into a
stream of 'struct Packet' now delivers those packets to a PacketQueue
called ssh->pq_full (containing the full, unfiltered stream of all
packets received on the SSH connection), replacing the old API in
which each coroutine would directly return a 'struct Packet *' to its
caller, or NULL if it didn't have one ready yet.
This simplifies the function-call API of the rdpkt coroutines (they
now return void). It increases the complexity at the other end,
because we've now got a function ssh_process_pq_full (scheduled as an
idempotent callback whenever rdpkt appends anything to the queue)
which pulls things out of the queue and passes them to ssh->protocol.
But that's only a temporary complexity increase; by the time I finish
the upcoming stream of refactorings, there won't be two chained
functions there any more.
One small workaround I had to add in this commit is a flag called
'pending_newkeys', which ssh2_rdpkt sets when it's just returned an
SSH_MSG_NEWKEYS packet, and then waits for the transport layer to
process the NEWKEYS and set up the new encryption context before
processing any more wire data. This wasn't necessary before, because
the old architecture was naturally synchronous - ssh2_rdpkt would
return a NEWKEYS, which would be immediately passed to
do_ssh2_transport, which would finish processing it immediately, and
by the time ssh2_rdpkt was next called, the keys would already be in
place.
This change adds a big while loop around the whole of each rdpkt
function, so it's easiest to read it as a whitespace-ignored diff.
2018-05-18 06:22:57 +00:00
|
|
|
ssh->pq_full_consumer.fn = ssh_process_pq_full;
|
|
|
|
ssh->pq_full_consumer.ctx = ssh;
|
|
|
|
ssh->pq_full_consumer.queued = FALSE;
|
2018-09-19 20:56:11 +00:00
|
|
|
pq_in_init(&ssh->pq_ssh1_login);
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->ssh1_login_icb.fn = do_ssh1_login;
|
|
|
|
ssh->ssh1_login_icb.ctx = ssh;
|
|
|
|
ssh->ssh1_login_icb.queued = FALSE;
|
2018-09-19 20:56:11 +00:00
|
|
|
pq_in_init(&ssh->pq_ssh1_connection);
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->ssh1_connection_icb.fn = do_ssh1_connection;
|
|
|
|
ssh->ssh1_connection_icb.ctx = ssh;
|
|
|
|
ssh->ssh1_connection_icb.queued = FALSE;
|
2018-09-19 20:56:11 +00:00
|
|
|
pq_in_init(&ssh->pq_ssh2_transport);
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->ssh2_transport_icb.fn = do_ssh2_transport;
|
|
|
|
ssh->ssh2_transport_icb.ctx = ssh;
|
|
|
|
ssh->ssh2_transport_icb.queued = FALSE;
|
2018-09-19 20:56:11 +00:00
|
|
|
pq_in_init(&ssh->pq_ssh2_userauth);
|
2018-05-18 06:22:59 +00:00
|
|
|
ssh->ssh2_userauth_icb.fn = do_ssh2_userauth;
|
|
|
|
ssh->ssh2_userauth_icb.ctx = ssh;
|
|
|
|
ssh->ssh2_userauth_icb.queued = FALSE;
|
2018-09-19 20:56:11 +00:00
|
|
|
pq_in_init(&ssh->pq_ssh2_connection);
|
2018-05-18 06:22:59 +00:00
|
|
|
ssh->ssh2_connection_icb.fn = do_ssh2_connection;
|
|
|
|
ssh->ssh2_connection_icb.ctx = ssh;
|
|
|
|
ssh->ssh2_connection_icb.queued = FALSE;
|
2018-05-18 06:22:58 +00:00
|
|
|
bufchain_init(&ssh->user_input);
|
|
|
|
ssh->user_input_consumer.fn = ssh_process_user_input;
|
|
|
|
ssh->user_input_consumer.ctx = ssh;
|
|
|
|
ssh->user_input_consumer.queued = FALSE;
|
2018-06-09 08:09:10 +00:00
|
|
|
bufchain_init(&ssh->outgoing_data);
|
|
|
|
ssh->outgoing_data_sender.fn = ssh_send_outgoing_data;
|
|
|
|
ssh->outgoing_data_sender.ctx = ssh;
|
|
|
|
ssh->outgoing_data_sender.queued = FALSE;
|
2018-05-18 10:41:17 +00:00
|
|
|
ssh->current_user_input_fn = NULL;
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->rekey_reason = NULL;
|
|
|
|
ssh->rekey_class = RK_INITIAL;
|
2005-08-30 20:38:57 +00:00
|
|
|
ssh->v_c = NULL;
|
|
|
|
ssh->v_s = NULL;
|
2002-10-29 14:41:10 +00:00
|
|
|
ssh->mainchan = NULL;
|
2002-11-01 12:59:09 +00:00
|
|
|
ssh->throttled_all = 0;
|
|
|
|
ssh->v1_stdout_throttling = 0;
|
2018-09-19 20:56:11 +00:00
|
|
|
pq_out_init(&ssh->outq);
|
2004-11-24 19:23:02 +00:00
|
|
|
ssh->queueing = FALSE;
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
ssh->qhead = ssh->qtail = NULL;
|
2004-12-31 10:51:14 +00:00
|
|
|
ssh->deferred_rekey_reason = NULL;
|
2005-02-17 18:34:24 +00:00
|
|
|
ssh->frozen = FALSE;
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
ssh->username = NULL;
|
2011-09-13 11:44:03 +00:00
|
|
|
ssh->sent_console_eof = FALSE;
|
2011-09-13 15:38:12 +00:00
|
|
|
ssh->got_pty = FALSE;
|
2013-11-17 14:05:41 +00:00
|
|
|
ssh->bare_connection = FALSE;
|
2014-11-22 14:57:06 +00:00
|
|
|
ssh->X11_fwd_enabled = FALSE;
|
|
|
|
ssh->connshare = NULL;
|
2013-11-17 14:05:41 +00:00
|
|
|
ssh->attempting_connshare = FALSE;
|
2015-11-22 14:33:28 +00:00
|
|
|
ssh->session_started = FALSE;
|
2016-03-21 06:50:50 +00:00
|
|
|
ssh->specials = NULL;
|
Add manual cross-certification of new host keys.
If a server offers host key algorithms that we don't have a stored key
for, they will now appear in a submenu of the Special Commands menu.
Selecting one will force a repeat key exchange with that key, and if
it succeeds, will add the new host key to the cache. The idea is that
the new key sent by the server is protected by the crypto established
in the previous key exchange, so this is just as safe as typing some
command like 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub' at the
server prompt and transcribing the results manually.
This allows switching over to newer host key algorithms if the client
has begun to support them (e.g. people using PuTTY's new ECC
functionality for the first time), or if the server has acquired a new
key (e.g. due to a server OS upgrade).
At the moment, it's only available manually, for a single host key
type at a time. Automating it is potentially controversial for
security policy reasons (what if someone doesn't agree this is what
they want in their host key cache, or doesn't want to switch over to
using whichever of the keys PuTTY would now put top of the list?), for
code plumbing reasons (chaining several of these rekeys might be more
annoying than doing one at a time) and for CPU usage reasons (rekeys
are expensive), but even so, it might turn out to be a good idea in
future.
2016-03-21 07:25:31 +00:00
|
|
|
ssh->n_uncert_hostkeys = 0;
|
|
|
|
ssh->cross_certifying = FALSE;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
#ifndef NO_GSSAPI
|
|
|
|
ssh->gss_cred_expiry = GSS_NO_EXPIRATION;
|
|
|
|
ssh->gss_srv_name = GSS_C_NO_NAME;
|
|
|
|
ssh->gss_ctx = NULL;
|
|
|
|
ssh_init_transient_hostkey_store(ssh);
|
|
|
|
#endif
|
|
|
|
ssh->gss_kex_used = FALSE;
|
|
|
|
|
2018-09-11 15:23:38 +00:00
|
|
|
ssh->backend.vt = &ssh_backend;
|
|
|
|
*backend_handle = &ssh->backend;
|
2001-05-06 14:35:20 +00:00
|
|
|
|
2018-09-12 08:10:51 +00:00
|
|
|
ssh->frontend = frontend;
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
ssh->term_width = conf_get_int(ssh->conf, CONF_width);
|
|
|
|
ssh->term_height = conf_get_int(ssh->conf, CONF_height);
|
2002-10-22 16:11:33 +00:00
|
|
|
|
2018-09-17 11:14:00 +00:00
|
|
|
ssh->cl.vt = &ssh_connlayer_vtable;
|
|
|
|
ssh->cl.frontend = ssh->frontend;
|
|
|
|
|
2003-01-15 23:30:21 +00:00
|
|
|
ssh->channels = NULL;
|
|
|
|
ssh->rportfwds = NULL;
|
2018-09-17 11:14:00 +00:00
|
|
|
ssh->portfwdmgr = portfwdmgr_new(&ssh->cl);
|
2003-01-15 23:30:21 +00:00
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->send_ok = 0;
|
|
|
|
ssh->editing = 0;
|
|
|
|
ssh->echoing = 0;
|
2007-10-03 20:29:27 +00:00
|
|
|
ssh->conn_throttle_count = 0;
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->overall_bufsize = 0;
|
|
|
|
ssh->fallback_cmd = 0;
|
2000-10-23 10:32:37 +00:00
|
|
|
|
2018-05-18 06:22:57 +00:00
|
|
|
ssh->general_packet_processing = NULL;
|
2003-01-07 22:45:12 +00:00
|
|
|
|
2004-11-27 13:20:21 +00:00
|
|
|
ssh->pinger = NULL;
|
|
|
|
|
2018-06-09 08:09:10 +00:00
|
|
|
ssh->incoming_data_size = ssh->outgoing_data_size = 0L;
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
ssh->max_data_size = parse_blocksize(conf_get_str(ssh->conf,
|
|
|
|
CONF_ssh_rekey_data));
|
2004-11-27 14:29:20 +00:00
|
|
|
ssh->kex_in_progress = FALSE;
|
|
|
|
|
2017-01-29 20:24:15 +00:00
|
|
|
ssh->auth_agent_query = NULL;
|
|
|
|
|
2010-09-25 07:16:56 +00:00
|
|
|
#ifndef NO_GSSAPI
|
|
|
|
ssh->gsslibs = NULL;
|
|
|
|
#endif
|
|
|
|
|
2014-09-09 12:47:39 +00:00
|
|
|
random_ref(); /* do this now - may be needed by sharing setup code */
|
2017-11-27 19:29:47 +00:00
|
|
|
ssh->need_random_unref = TRUE;
|
2014-09-09 12:47:39 +00:00
|
|
|
|
2004-06-20 17:07:38 +00:00
|
|
|
p = connect_to_host(ssh, host, port, realhost, nodelay, keepalive);
|
2014-09-09 12:47:39 +00:00
|
|
|
if (p != NULL) {
|
2017-11-27 19:29:47 +00:00
|
|
|
/* Call random_unref now instead of waiting until the caller
|
|
|
|
* frees this useless Ssh object, in case the caller is
|
|
|
|
* impatient and just exits without bothering, in which case
|
|
|
|
* the random seed won't be re-saved. */
|
|
|
|
ssh->need_random_unref = FALSE;
|
2014-09-09 12:47:39 +00:00
|
|
|
random_unref();
|
2000-06-22 12:18:34 +00:00
|
|
|
return p;
|
2014-09-09 12:47:39 +00:00
|
|
|
}
|
2004-11-27 19:56:38 +00:00
|
|
|
|
1999-01-08 13:02:13 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-09-11 15:23:38 +00:00
|
|
|
static void ssh_free(Backend *be)
|
2003-01-15 23:30:21 +00:00
|
|
|
{
|
2018-09-11 15:23:38 +00:00
|
|
|
Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
|
2003-01-15 23:30:21 +00:00
|
|
|
struct ssh_channel *c;
|
|
|
|
struct ssh_rportfwd *pf;
|
2013-11-17 14:05:10 +00:00
|
|
|
struct X11FakeAuth *auth;
|
2017-11-27 19:29:47 +00:00
|
|
|
int need_random_unref;
|
2003-01-15 23:30:21 +00:00
|
|
|
|
2018-09-14 07:48:54 +00:00
|
|
|
if (ssh->dh_ctx)
|
|
|
|
dh_cleanup(ssh->dh_ctx);
|
2003-01-15 23:30:21 +00:00
|
|
|
sfree(ssh->savedhost);
|
|
|
|
|
2018-09-19 20:56:11 +00:00
|
|
|
pq_out_clear(&ssh->outq);
|
2004-11-24 19:23:02 +00:00
|
|
|
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
while (ssh->qhead) {
|
|
|
|
struct queued_handler *qh = ssh->qhead;
|
|
|
|
ssh->qhead = qh->next;
|
2013-07-01 17:56:33 +00:00
|
|
|
sfree(qh);
|
Abstracted out the rather large port-forwarding setup code into a
routine which is common between SSH1 and SSH2. Since this routine is
not part of the coroutine system, this means it can't sit and wait
to get its various success/failure responses back. Hence, I've
introduced a system of queued packet handlers, each of which waits
for exactly one of a pair of messages (SSH1_SMSG_{SUCCESS,FAILURE}
or SSH2_MSG_REQUEST_{SUCCESS,FAILURE}), handles it when it arrives,
and automatically de-registers itself. Hence the port-forwarding
setup code can be called once, and then subsequent packets related
to it will automatically be handled as they arrive.
The real purpose of all this is that the infrastructure is now there
for me to arrange mid-session configurability of port forwarding.
However, a side benefit is that fewer round trips are involved in
session startup. I'd quite like to move more of the connection setup
(X forwarding, agent forwarding, pty allocation etc) to using the
new queued handler mechanism for this reason.
[originally from svn r5029]
2004-12-28 14:04:58 +00:00
|
|
|
}
|
|
|
|
ssh->qhead = ssh->qtail = NULL;
|
|
|
|
|
2003-01-15 23:30:21 +00:00
|
|
|
if (ssh->channels) {
|
|
|
|
while ((c = delpos234(ssh->channels, 0)) != NULL) {
|
2016-05-28 13:50:02 +00:00
|
|
|
ssh_channel_close_local(c, NULL);
|
2012-08-25 21:06:48 +00:00
|
|
|
if (ssh->version == 2) {
|
|
|
|
struct outstanding_channel_request *ocr, *nocr;
|
|
|
|
ocr = c->v.v2.chanreq_head;
|
|
|
|
while (ocr) {
|
|
|
|
ocr->handler(c, NULL, ocr->ctx);
|
|
|
|
nocr = ocr->next;
|
|
|
|
sfree(ocr);
|
|
|
|
ocr = nocr;
|
|
|
|
}
|
|
|
|
bufchain_clear(&c->v.v2.outbuffer);
|
|
|
|
}
|
2003-01-15 23:30:21 +00:00
|
|
|
sfree(c);
|
|
|
|
}
|
|
|
|
freetree234(ssh->channels);
|
2004-12-02 13:37:28 +00:00
|
|
|
ssh->channels = NULL;
|
2003-01-15 23:30:21 +00:00
|
|
|
}
|
|
|
|
|
2018-05-18 10:41:17 +00:00
|
|
|
if (ssh->connshare) {
|
2013-11-17 14:05:41 +00:00
|
|
|
sharestate_free(ssh->connshare);
|
2018-05-18 10:41:17 +00:00
|
|
|
ssh->connshare = NULL;
|
|
|
|
}
|
2013-11-17 14:05:41 +00:00
|
|
|
|
2003-01-15 23:30:21 +00:00
|
|
|
if (ssh->rportfwds) {
|
|
|
|
while ((pf = delpos234(ssh->rportfwds, 0)) != NULL)
|
2010-07-30 20:47:03 +00:00
|
|
|
free_rportfwd(pf);
|
2003-01-15 23:30:21 +00:00
|
|
|
freetree234(ssh->rportfwds);
|
2004-12-02 13:37:28 +00:00
|
|
|
ssh->rportfwds = NULL;
|
2003-01-15 23:30:21 +00:00
|
|
|
}
|
2018-09-14 16:04:39 +00:00
|
|
|
portfwdmgr_free(ssh->portfwdmgr);
|
2008-11-17 18:38:09 +00:00
|
|
|
if (ssh->x11disp)
|
|
|
|
x11_free_display(ssh->x11disp);
|
2013-11-17 14:05:10 +00:00
|
|
|
while ((auth = delpos234(ssh->x11authtree, 0)) != NULL)
|
|
|
|
x11_free_fake_auth(auth);
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
if (ssh->bpp)
|
|
|
|
ssh_bpp_free(ssh->bpp);
|
2013-11-17 14:05:10 +00:00
|
|
|
freetree234(ssh->x11authtree);
|
2003-01-15 23:30:21 +00:00
|
|
|
sfree(ssh->do_ssh_init_state);
|
|
|
|
sfree(ssh->do_ssh1_login_state);
|
|
|
|
sfree(ssh->do_ssh2_transport_state);
|
2018-05-18 06:22:59 +00:00
|
|
|
sfree(ssh->do_ssh2_userauth_state);
|
|
|
|
sfree(ssh->do_ssh2_connection_state);
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
bufchain_clear(&ssh->incoming_data);
|
2018-06-09 08:09:10 +00:00
|
|
|
bufchain_clear(&ssh->outgoing_data);
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
sfree(ssh->incoming_data_eof_message);
|
2018-09-19 20:56:11 +00:00
|
|
|
pq_in_clear(&ssh->pq_full);
|
|
|
|
pq_in_clear(&ssh->pq_ssh1_login);
|
|
|
|
pq_in_clear(&ssh->pq_ssh1_connection);
|
|
|
|
pq_in_clear(&ssh->pq_ssh2_transport);
|
|
|
|
pq_in_clear(&ssh->pq_ssh2_userauth);
|
|
|
|
pq_in_clear(&ssh->pq_ssh2_connection);
|
2018-05-18 06:22:58 +00:00
|
|
|
bufchain_clear(&ssh->user_input);
|
2005-08-30 20:38:57 +00:00
|
|
|
sfree(ssh->v_c);
|
|
|
|
sfree(ssh->v_s);
|
2008-08-10 13:10:31 +00:00
|
|
|
sfree(ssh->fullhostname);
|
2013-08-18 06:48:20 +00:00
|
|
|
sfree(ssh->hostkey_str);
|
2016-03-21 06:50:50 +00:00
|
|
|
sfree(ssh->specials);
|
2003-01-15 23:30:21 +00:00
|
|
|
if (ssh->s)
|
2005-01-11 19:33:41 +00:00
|
|
|
ssh_do_close(ssh, TRUE);
|
2004-11-27 14:29:20 +00:00
|
|
|
expire_timer_context(ssh);
|
2004-11-27 13:20:21 +00:00
|
|
|
if (ssh->pinger)
|
|
|
|
pinger_free(ssh->pinger);
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
sfree(ssh->username);
|
|
|
|
conf_free(ssh->conf);
|
2017-01-29 20:24:15 +00:00
|
|
|
|
|
|
|
if (ssh->auth_agent_query)
|
|
|
|
agent_cancel_query(ssh->auth_agent_query);
|
|
|
|
|
2010-09-25 07:16:56 +00:00
|
|
|
#ifndef NO_GSSAPI
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
if (ssh->gss_srv_name)
|
|
|
|
ssh->gsslib->release_name(ssh->gsslib, &ssh->gss_srv_name);
|
|
|
|
if (ssh->gss_ctx != NULL)
|
|
|
|
ssh->gsslib->release_cred(ssh->gsslib, &ssh->gss_ctx);
|
2010-09-25 07:16:56 +00:00
|
|
|
if (ssh->gsslibs)
|
|
|
|
ssh_gss_cleanup(ssh->gsslibs);
|
Support GSS key exchange, for Kerberos 5 only.
This is a heavily edited (by me) version of a patch originally due to
Nico Williams and Viktor Dukhovni. Their comments:
* Don't delegate credentials when rekeying unless there's a new TGT
or the old service ticket is nearly expired.
* Check for the above conditions more frequently (every two minutes
by default) and rekey when we would delegate credentials.
* Do not rekey with very short service ticket lifetimes; some GSSAPI
libraries may lose the race to use an almost expired ticket. Adjust
the timing of rekey checks to try to avoid this possibility.
My further comments:
The most interesting thing about this patch to me is that the use of
GSS key exchange causes a switch over to a completely different model
of what host keys are for. This comes from RFC 4462 section 2.1: the
basic idea is that when your session is mostly bidirectionally
authenticated by the GSSAPI exchanges happening in initial kex and
every rekey, host keys become more or less vestigial, and their
remaining purpose is to allow a rekey to happen if the requirements of
the SSH protocol demand it at an awkward moment when the GSS
credentials are not currently available (e.g. timed out and haven't
been renewed yet). As such, there's no need for host keys to be
_permanent_ or to be a reliable identifier of a particular host, and
RFC 4462 allows for the possibility that they might be purely
transient and only for this kind of emergency fallback purpose.
Therefore, once PuTTY has done a GSS key exchange, it disconnects
itself completely from the permanent host key cache functions in
storage.h, and instead switches to a _transient_ host key cache stored
in memory with the lifetime of just that SSH session. That cache is
populated with keys received from the server as a side effect of GSS
kex (via the optional SSH2_MSG_KEXGSS_HOSTKEY message), and used if
later in the session we have to fall back to a non-GSS key exchange.
However, in practice servers we've tested against do not send a host
key in that way, so we also have a fallback method of populating the
transient cache by triggering an immediate non-GSS rekey straight
after userauth (reusing the code path we also use to turn on OpenSSH
delayed encryption without the race condition).
2018-04-26 06:18:59 +00:00
|
|
|
ssh_cleanup_transient_hostkey_store(ssh);
|
2010-09-25 07:16:56 +00:00
|
|
|
#endif
|
2017-11-27 19:29:47 +00:00
|
|
|
need_random_unref = ssh->need_random_unref;
|
2004-12-16 15:38:39 +00:00
|
|
|
sfree(ssh);
|
2004-11-27 19:56:38 +00:00
|
|
|
|
2017-11-27 19:29:47 +00:00
|
|
|
if (need_random_unref)
|
|
|
|
random_unref();
|
2003-01-15 23:30:21 +00:00
|
|
|
}
|
|
|
|
|
2003-01-12 14:48:29 +00:00
|
|
|
/*
|
|
|
|
* Reconfigure the SSH backend.
|
|
|
|
*/
|
2018-09-11 15:23:38 +00:00
|
|
|
static void ssh_reconfig(Backend *be, Conf *conf)
|
2003-01-12 14:48:29 +00:00
|
|
|
{
|
2018-09-11 15:23:38 +00:00
|
|
|
Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
|
2015-05-15 10:15:42 +00:00
|
|
|
const char *rekeying = NULL;
|
|
|
|
int rekey_mandatory = FALSE;
|
2004-12-28 14:10:32 +00:00
|
|
|
unsigned long old_max_data_size;
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
int i, rekey_time;
|
2004-12-28 14:10:32 +00:00
|
|
|
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
pinger_reconfig(ssh->pinger, ssh->conf, conf);
|
2018-09-14 16:04:39 +00:00
|
|
|
portfwdmgr_config(ssh->portfwdmgr, conf);
|
2004-12-28 14:10:32 +00:00
|
|
|
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
rekey_time = conf_get_int(conf, CONF_ssh_rekey_time);
|
2018-04-14 13:48:02 +00:00
|
|
|
if (ssh2_timer_update(ssh, rekey_mins(rekey_time, 60)))
|
|
|
|
rekeying = "timeout shortened";
|
2004-12-28 14:10:32 +00:00
|
|
|
|
|
|
|
old_max_data_size = ssh->max_data_size;
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
ssh->max_data_size = parse_blocksize(conf_get_str(ssh->conf,
|
|
|
|
CONF_ssh_rekey_data));
|
2004-12-28 14:10:32 +00:00
|
|
|
if (old_max_data_size != ssh->max_data_size &&
|
|
|
|
ssh->max_data_size != 0) {
|
|
|
|
if (ssh->outgoing_data_size > ssh->max_data_size ||
|
|
|
|
ssh->incoming_data_size > ssh->max_data_size)
|
2005-01-11 10:37:55 +00:00
|
|
|
rekeying = "data limit lowered";
|
2004-12-28 14:10:32 +00:00
|
|
|
}
|
|
|
|
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (conf_get_int(ssh->conf, CONF_compression) !=
|
|
|
|
conf_get_int(conf, CONF_compression)) {
|
2005-01-11 10:37:55 +00:00
|
|
|
rekeying = "compression setting changed";
|
2004-12-31 10:51:14 +00:00
|
|
|
rekey_mandatory = TRUE;
|
|
|
|
}
|
|
|
|
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
for (i = 0; i < CIPHER_MAX; i++)
|
|
|
|
if (conf_get_int_int(ssh->conf, CONF_ssh_cipherlist, i) !=
|
|
|
|
conf_get_int_int(conf, CONF_ssh_cipherlist, i)) {
|
|
|
|
rekeying = "cipher settings changed";
|
|
|
|
rekey_mandatory = TRUE;
|
|
|
|
}
|
|
|
|
if (conf_get_int(ssh->conf, CONF_ssh2_des_cbc) !=
|
|
|
|
conf_get_int(conf, CONF_ssh2_des_cbc)) {
|
2005-01-11 10:37:55 +00:00
|
|
|
rekeying = "cipher settings changed";
|
2004-12-31 10:51:14 +00:00
|
|
|
rekey_mandatory = TRUE;
|
2004-12-28 14:10:32 +00:00
|
|
|
}
|
|
|
|
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
conf_free(ssh->conf);
|
|
|
|
ssh->conf = conf_copy(conf);
|
|
|
|
ssh_cache_conf_values(ssh);
|
2004-12-31 10:51:14 +00:00
|
|
|
|
2013-11-17 14:05:41 +00:00
|
|
|
if (!ssh->bare_connection && rekeying) {
|
2004-12-31 10:51:14 +00:00
|
|
|
if (!ssh->kex_in_progress) {
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->rekey_reason = rekeying;
|
|
|
|
ssh->rekey_class = RK_NORMAL;
|
2018-05-18 06:22:58 +00:00
|
|
|
queue_idempotent_callback(&ssh->ssh2_transport_icb);
|
2004-12-31 10:51:14 +00:00
|
|
|
} else if (rekey_mandatory) {
|
|
|
|
ssh->deferred_rekey_reason = rekeying;
|
|
|
|
}
|
|
|
|
}
|
2003-01-12 14:48:29 +00:00
|
|
|
}
|
|
|
|
|
1999-01-08 13:02:13 +00:00
|
|
|
/*
|
2005-05-28 13:46:04 +00:00
|
|
|
* Called to send data down the SSH connection.
|
1999-01-08 13:02:13 +00:00
|
|
|
*/
|
2018-09-11 15:23:38 +00:00
|
|
|
static int ssh_send(Backend *be, const char *buf, int len)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2018-09-11 15:23:38 +00:00
|
|
|
Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
|
2002-10-25 11:30:33 +00:00
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
if (ssh == NULL || ssh->s == NULL)
|
2001-08-25 17:09:23 +00:00
|
|
|
return 0;
|
1999-01-08 13:02:13 +00:00
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
bufchain_add(&ssh->user_input, buf, len);
|
|
|
|
queue_idempotent_callback(&ssh->user_input_consumer);
|
2001-08-25 17:09:23 +00:00
|
|
|
|
2018-09-11 15:23:38 +00:00
|
|
|
return backend_sendbuffer(&ssh->backend);
|
2001-08-25 17:09:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called to query the current amount of buffered stdin data.
|
|
|
|
*/
|
2018-09-11 15:23:38 +00:00
|
|
|
static int ssh_sendbuffer(Backend *be)
|
2001-08-25 17:09:23 +00:00
|
|
|
{
|
2018-09-11 15:23:38 +00:00
|
|
|
Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
|
2001-08-25 17:09:23 +00:00
|
|
|
int override_value;
|
|
|
|
|
2018-05-18 06:22:58 +00:00
|
|
|
if (ssh == NULL || ssh->s == NULL)
|
2001-08-25 17:09:23 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the SSH socket itself has backed up, add the total backup
|
|
|
|
* size on that to any individual buffer on the stdin channel.
|
|
|
|
*/
|
|
|
|
override_value = 0;
|
2002-10-25 11:30:33 +00:00
|
|
|
if (ssh->throttled_all)
|
|
|
|
override_value = ssh->overall_bufsize;
|
2001-08-25 17:09:23 +00:00
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
if (ssh->version == 1) {
|
2001-08-25 17:09:23 +00:00
|
|
|
return override_value;
|
2002-10-25 11:30:33 +00:00
|
|
|
} else if (ssh->version == 2) {
|
2011-09-13 11:44:03 +00:00
|
|
|
if (!ssh->mainchan)
|
2001-08-25 17:09:23 +00:00
|
|
|
return override_value;
|
|
|
|
else
|
2002-10-25 11:30:33 +00:00
|
|
|
return (override_value +
|
|
|
|
bufchain_size(&ssh->mainchan->v.v2.outbuffer));
|
2001-08-25 17:09:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
1999-01-08 13:02:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2000-10-24 09:49:03 +00:00
|
|
|
* Called to set the size of the window from SSH's POV.
|
1999-01-08 13:02:13 +00:00
|
|
|
*/
|
2018-09-11 15:23:38 +00:00
|
|
|
static void ssh_size(Backend *be, int width, int height)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2018-09-11 15:23:38 +00:00
|
|
|
Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
|
|
|
ssh->term_width = width;
|
|
|
|
ssh->term_height = height;
|
2002-10-23 12:41:35 +00:00
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
switch (ssh->state) {
|
1999-01-08 13:02:13 +00:00
|
|
|
case SSH_STATE_BEFORE_SIZE:
|
2001-01-08 13:57:45 +00:00
|
|
|
case SSH_STATE_PREPACKET:
|
1999-03-23 17:21:44 +00:00
|
|
|
case SSH_STATE_CLOSED:
|
1999-01-08 13:02:13 +00:00
|
|
|
break; /* do nothing */
|
|
|
|
case SSH_STATE_INTERMED:
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->size_needed = TRUE; /* buffer for later */
|
1999-01-08 13:02:13 +00:00
|
|
|
break;
|
|
|
|
case SSH_STATE_SESSION:
|
Post-release destabilisation! Completely remove the struct type
'Config' in putty.h, which stores all PuTTY's settings and includes an
arbitrary length limit on every single one of those settings which is
stored in string form. In place of it is 'Conf', an opaque data type
everywhere outside the new file conf.c, which stores a list of (key,
value) pairs in which every key contains an integer identifying a
configuration setting, and for some of those integers the key also
contains extra parts (so that, for instance, CONF_environmt is a
string-to-string mapping). Everywhere that a Config was previously
used, a Conf is now; everywhere there was a Config structure copy,
conf_copy() is called; every lookup, adjustment, load and save
operation on a Config has been rewritten; and there's a mechanism for
serialising a Conf into a binary blob and back for use with Duplicate
Session.
User-visible effects of this change _should_ be minimal, though I
don't doubt I've introduced one or two bugs here and there which will
eventually be found. The _intended_ visible effects of this change are
that all arbitrary limits on configuration strings and lists (e.g.
limit on number of port forwardings) should now disappear; that list
boxes in the configuration will now be displayed in a sorted order
rather than the arbitrary order in which they were added to the list
(since the underlying data structure is now a sorted tree234 rather
than an ad-hoc comma-separated string); and one more specific change,
which is that local and dynamic port forwardings on the same port
number are now mutually exclusive in the configuration (putting 'D' in
the key rather than the value was a mistake in the first place).
One other reorganisation as a result of this is that I've moved all
the dialog.c standard handlers (dlg_stdeditbox_handler and friends)
out into config.c, because I can't really justify calling them generic
any more. When they took a pointer to an arbitrary structure type and
the offset of a field within that structure, they were independent of
whether that structure was a Config or something completely different,
but now they really do expect to talk to a Conf, which can _only_ be
used for PuTTY configuration, so I've renamed them all things like
conf_editbox_handler and moved them out of the nominally independent
dialog-box management module into the PuTTY-specific config.c.
[originally from svn r9214]
2011-07-14 18:52:21 +00:00
|
|
|
if (!conf_get_int(ssh->conf, CONF_nopty)) {
|
2002-10-25 11:30:33 +00:00
|
|
|
if (ssh->version == 1) {
|
2018-06-09 08:59:58 +00:00
|
|
|
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_WINDOW_SIZE);
|
|
|
|
put_uint32(pktout, ssh->term_height);
|
|
|
|
put_uint32(pktout, ssh->term_width);
|
|
|
|
put_uint32(pktout, 0);
|
|
|
|
put_uint32(pktout, 0);
|
|
|
|
ssh_pkt_write(ssh, pktout);
|
2004-10-13 13:43:11 +00:00
|
|
|
} else if (ssh->mainchan) {
|
2012-08-25 15:57:05 +00:00
|
|
|
pktout = ssh2_chanreq_init(ssh->mainchan, "window-change",
|
|
|
|
NULL, NULL);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(pktout, ssh->term_width);
|
|
|
|
put_uint32(pktout, ssh->term_height);
|
|
|
|
put_uint32(pktout, 0);
|
|
|
|
put_uint32(pktout, 0);
|
2004-11-24 18:45:52 +00:00
|
|
|
ssh2_pkt_send(ssh, pktout);
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
1999-01-08 13:02:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-04-04 20:21:05 +00:00
|
|
|
/*
|
|
|
|
* Return a list of the special codes that make sense in this
|
|
|
|
* protocol.
|
|
|
|
*/
|
2018-09-11 15:23:38 +00:00
|
|
|
static const struct telnet_special *ssh_get_specials(Backend *be)
|
2003-04-04 20:21:05 +00:00
|
|
|
{
|
2004-12-24 10:04:28 +00:00
|
|
|
static const struct telnet_special ssh1_ignore_special[] = {
|
|
|
|
{"IGNORE message", TS_NOP}
|
|
|
|
};
|
2010-04-23 18:32:15 +00:00
|
|
|
static const struct telnet_special ssh2_ignore_special[] = {
|
2004-10-17 15:32:42 +00:00
|
|
|
{"IGNORE message", TS_NOP},
|
2010-04-23 18:32:15 +00:00
|
|
|
};
|
|
|
|
static const struct telnet_special ssh2_rekey_special[] = {
|
2004-11-27 14:29:20 +00:00
|
|
|
{"Repeat key exchange", TS_REKEY},
|
2004-10-17 15:32:42 +00:00
|
|
|
};
|
|
|
|
static const struct telnet_special ssh2_session_specials[] = {
|
2004-10-17 21:22:22 +00:00
|
|
|
{NULL, TS_SEP},
|
|
|
|
{"Break", TS_BRK},
|
2007-10-03 21:21:18 +00:00
|
|
|
/* These are the signal names defined by RFC 4254.
|
2004-10-17 21:22:22 +00:00
|
|
|
* They include all the ISO C signals, but are a subset of the POSIX
|
|
|
|
* required signals. */
|
|
|
|
{"SIGINT (Interrupt)", TS_SIGINT},
|
|
|
|
{"SIGTERM (Terminate)", TS_SIGTERM},
|
|
|
|
{"SIGKILL (Kill)", TS_SIGKILL},
|
|
|
|
{"SIGQUIT (Quit)", TS_SIGQUIT},
|
|
|
|
{"SIGHUP (Hangup)", TS_SIGHUP},
|
|
|
|
{"More signals", TS_SUBMENU},
|
|
|
|
{"SIGABRT", TS_SIGABRT}, {"SIGALRM", TS_SIGALRM},
|
|
|
|
{"SIGFPE", TS_SIGFPE}, {"SIGILL", TS_SIGILL},
|
|
|
|
{"SIGPIPE", TS_SIGPIPE}, {"SIGSEGV", TS_SIGSEGV},
|
|
|
|
{"SIGUSR1", TS_SIGUSR1}, {"SIGUSR2", TS_SIGUSR2},
|
|
|
|
{NULL, TS_EXITMENU}
|
2004-10-17 15:32:42 +00:00
|
|
|
};
|
|
|
|
static const struct telnet_special specials_end[] = {
|
2004-10-17 21:22:22 +00:00
|
|
|
{NULL, TS_EXITMENU}
|
2004-10-17 15:32:42 +00:00
|
|
|
};
|
2016-03-21 06:50:50 +00:00
|
|
|
|
|
|
|
struct telnet_special *specials = NULL;
|
|
|
|
int nspecials = 0, specialsize = 0;
|
|
|
|
|
2018-09-11 15:23:38 +00:00
|
|
|
Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
|
2016-03-21 06:50:50 +00:00
|
|
|
|
|
|
|
sfree(ssh->specials);
|
|
|
|
|
|
|
|
#define ADD_SPECIALS(name) do \
|
|
|
|
{ \
|
|
|
|
int len = lenof(name); \
|
|
|
|
if (nspecials + len > specialsize) { \
|
|
|
|
specialsize = (nspecials + len) * 5 / 4 + 32; \
|
|
|
|
specials = sresize(specials, specialsize, struct telnet_special); \
|
|
|
|
} \
|
|
|
|
memcpy(specials+nspecials, name, len*sizeof(struct telnet_special)); \
|
|
|
|
nspecials += len; \
|
|
|
|
} while (0)
|
2003-04-04 20:21:05 +00:00
|
|
|
|
|
|
|
if (ssh->version == 1) {
|
2004-10-17 15:32:42 +00:00
|
|
|
/* Don't bother offering IGNORE if we've decided the remote
|
|
|
|
* won't cope with it, since we wouldn't bother sending it if
|
|
|
|
* asked anyway. */
|
|
|
|
if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
|
2004-12-24 10:04:28 +00:00
|
|
|
ADD_SPECIALS(ssh1_ignore_special);
|
2003-04-04 20:21:05 +00:00
|
|
|
} else if (ssh->version == 2) {
|
2010-04-23 18:32:15 +00:00
|
|
|
if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE))
|
|
|
|
ADD_SPECIALS(ssh2_ignore_special);
|
2013-11-17 14:05:41 +00:00
|
|
|
if (!(ssh->remote_bugs & BUG_SSH2_REKEY) && !ssh->bare_connection)
|
2010-04-23 18:32:15 +00:00
|
|
|
ADD_SPECIALS(ssh2_rekey_special);
|
2004-10-17 15:32:42 +00:00
|
|
|
if (ssh->mainchan)
|
|
|
|
ADD_SPECIALS(ssh2_session_specials);
|
Add manual cross-certification of new host keys.
If a server offers host key algorithms that we don't have a stored key
for, they will now appear in a submenu of the Special Commands menu.
Selecting one will force a repeat key exchange with that key, and if
it succeeds, will add the new host key to the cache. The idea is that
the new key sent by the server is protected by the crypto established
in the previous key exchange, so this is just as safe as typing some
command like 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub' at the
server prompt and transcribing the results manually.
This allows switching over to newer host key algorithms if the client
has begun to support them (e.g. people using PuTTY's new ECC
functionality for the first time), or if the server has acquired a new
key (e.g. due to a server OS upgrade).
At the moment, it's only available manually, for a single host key
type at a time. Automating it is potentially controversial for
security policy reasons (what if someone doesn't agree this is what
they want in their host key cache, or doesn't want to switch over to
using whichever of the keys PuTTY would now put top of the list?), for
code plumbing reasons (chaining several of these rekeys might be more
annoying than doing one at a time) and for CPU usage reasons (rekeys
are expensive), but even so, it might turn out to be a good idea in
future.
2016-03-21 07:25:31 +00:00
|
|
|
|
|
|
|
if (ssh->n_uncert_hostkeys) {
|
|
|
|
static const struct telnet_special uncert_start[] = {
|
|
|
|
{NULL, TS_SEP},
|
|
|
|
{"Cache new host key type", TS_SUBMENU},
|
|
|
|
};
|
|
|
|
static const struct telnet_special uncert_end[] = {
|
|
|
|
{NULL, TS_EXITMENU},
|
|
|
|
};
|
|
|
|
int i;
|
|
|
|
|
|
|
|
ADD_SPECIALS(uncert_start);
|
|
|
|
for (i = 0; i < ssh->n_uncert_hostkeys; i++) {
|
|
|
|
struct telnet_special uncert[1];
|
Invent a struct type for polymorphic SSH key data.
During last week's work, I made a mistake in which I got the arguments
backwards in one of the key-blob-generating functions - mistakenly
swapped the 'void *' key instance with the 'BinarySink *' output
destination - and I didn't spot the mistake until run time, because in
C you can implicitly convert both to and from void * and so there was
no compile-time failure of type checking.
Now that I've introduced the FROMFIELD macro that downcasts a pointer
to one field of a structure to retrieve a pointer to the whole
structure, I think I might start using that more widely to indicate
this kind of polymorphic subtyping. So now all the public-key
functions in the struct ssh_signkey vtable handle their data instance
in the form of a pointer to a subfield of a new zero-sized structure
type 'ssh_key', which outside the key implementations indicates 'this
is some kind of key instance but it could be of any type'; they
downcast that pointer internally using FROMFIELD in place of the
previous ordinary C cast, and return one by returning &foo->sshk for
whatever foo they've just made up.
The sshk member is not at the beginning of the structure, which means
all those FROMFIELDs and &key->sshk are actually adding and
subtracting an offset. Of course I could have put the member at the
start anyway, but I had the idea that it's actually a feature _not_ to
have the two types start at the same address, because it means you
should notice earlier rather than later if you absentmindedly cast
from one to the other directly rather than by the approved method (in
particular, if you accidentally assign one through a void * and back
without even _noticing_ you perpetrated a cast). In particular, this
enforces that you can't sfree() the thing even once without realising
you should instead of called the right freekey function. (I found
several bugs by this method during initial testing, so I think it's
already proved its worth!)
While I'm here, I've also renamed the vtable structure ssh_signkey to
ssh_keyalg, because it was a confusing name anyway - it describes the
_algorithm_ for handling all keys of that type, not a specific key. So
ssh_keyalg is the collection of code, and ssh_key is one instance of
the data it handles.
2018-05-27 07:32:21 +00:00
|
|
|
const ssh_keyalg *alg =
|
2016-03-25 15:56:31 +00:00
|
|
|
hostkey_algs[ssh->uncert_hostkeys[i]].alg;
|
2018-06-03 11:58:05 +00:00
|
|
|
uncert[0].name = alg->ssh_id;
|
2016-03-21 18:59:01 +00:00
|
|
|
uncert[0].code = TS_LOCALSTART + ssh->uncert_hostkeys[i];
|
Add manual cross-certification of new host keys.
If a server offers host key algorithms that we don't have a stored key
for, they will now appear in a submenu of the Special Commands menu.
Selecting one will force a repeat key exchange with that key, and if
it succeeds, will add the new host key to the cache. The idea is that
the new key sent by the server is protected by the crypto established
in the previous key exchange, so this is just as safe as typing some
command like 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub' at the
server prompt and transcribing the results manually.
This allows switching over to newer host key algorithms if the client
has begun to support them (e.g. people using PuTTY's new ECC
functionality for the first time), or if the server has acquired a new
key (e.g. due to a server OS upgrade).
At the moment, it's only available manually, for a single host key
type at a time. Automating it is potentially controversial for
security policy reasons (what if someone doesn't agree this is what
they want in their host key cache, or doesn't want to switch over to
using whichever of the keys PuTTY would now put top of the list?), for
code plumbing reasons (chaining several of these rekeys might be more
annoying than doing one at a time) and for CPU usage reasons (rekeys
are expensive), but even so, it might turn out to be a good idea in
future.
2016-03-21 07:25:31 +00:00
|
|
|
ADD_SPECIALS(uncert);
|
|
|
|
}
|
|
|
|
ADD_SPECIALS(uncert_end);
|
|
|
|
}
|
2004-10-17 15:32:42 +00:00
|
|
|
} /* else we're not ready yet */
|
|
|
|
|
2016-03-21 06:50:50 +00:00
|
|
|
if (nspecials)
|
2004-10-17 15:32:42 +00:00
|
|
|
ADD_SPECIALS(specials_end);
|
2016-03-21 06:50:50 +00:00
|
|
|
|
|
|
|
ssh->specials = specials;
|
|
|
|
|
|
|
|
if (nspecials) {
|
|
|
|
return specials;
|
2004-10-17 15:32:42 +00:00
|
|
|
} else {
|
2003-04-04 20:21:05 +00:00
|
|
|
return NULL;
|
2004-10-17 15:32:42 +00:00
|
|
|
}
|
|
|
|
#undef ADD_SPECIALS
|
2003-04-04 20:21:05 +00:00
|
|
|
}
|
|
|
|
|
1999-01-08 13:02:13 +00:00
|
|
|
/*
|
2005-05-28 13:46:04 +00:00
|
|
|
* Send special codes. TS_EOF is useful for `plink', so you
|
2000-09-08 15:24:19 +00:00
|
|
|
* can send an EOF and collect resulting output (e.g. `plink
|
|
|
|
* hostname sort').
|
1999-01-08 13:02:13 +00:00
|
|
|
*/
|
2018-09-11 15:23:38 +00:00
|
|
|
static void ssh_special(Backend *be, Telnet_Special code)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2018-09-11 15:23:38 +00:00
|
|
|
Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
2002-10-25 11:30:33 +00:00
|
|
|
|
2000-09-08 15:24:19 +00:00
|
|
|
if (code == TS_EOF) {
|
2002-10-25 11:30:33 +00:00
|
|
|
if (ssh->state != SSH_STATE_SESSION) {
|
2001-05-06 14:35:20 +00:00
|
|
|
/*
|
|
|
|
* Buffer the EOF in case we are pre-SESSION, so we can
|
|
|
|
* send it as soon as we reach SESSION.
|
|
|
|
*/
|
|
|
|
if (code == TS_EOF)
|
2002-10-25 11:30:33 +00:00
|
|
|
ssh->eof_needed = TRUE;
|
2001-05-06 14:35:20 +00:00
|
|
|
return;
|
|
|
|
}
|
2002-10-25 11:30:33 +00:00
|
|
|
if (ssh->version == 1) {
|
2018-06-09 08:59:58 +00:00
|
|
|
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH1_CMSG_EOF);
|
|
|
|
ssh_pkt_write(ssh, pktout);
|
2004-10-13 13:43:11 +00:00
|
|
|
} else if (ssh->mainchan) {
|
2018-09-14 12:47:13 +00:00
|
|
|
sshfwd_write_eof(&ssh->mainchan->sc);
|
2005-03-29 13:10:33 +00:00
|
|
|
ssh->send_ok = 0; /* now stop trying to read from stdin */
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
|
|
|
logevent("Sent EOF message");
|
2003-04-04 20:21:05 +00:00
|
|
|
} else if (code == TS_PING || code == TS_NOP) {
|
2002-10-25 11:30:33 +00:00
|
|
|
if (ssh->state == SSH_STATE_CLOSED
|
|
|
|
|| ssh->state == SSH_STATE_PREPACKET) return;
|
|
|
|
if (ssh->version == 1) {
|
2018-06-09 08:59:58 +00:00
|
|
|
if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) {
|
|
|
|
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_IGNORE);
|
|
|
|
put_stringz(pktout, "");
|
|
|
|
ssh_pkt_write(ssh, pktout);
|
|
|
|
}
|
2001-05-06 14:35:20 +00:00
|
|
|
} else {
|
2010-04-23 18:32:15 +00:00
|
|
|
if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_IGNORE);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(pktout, "");
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
ssh_pkt_write(ssh, pktout);
|
2010-04-23 18:32:15 +00:00
|
|
|
}
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
2004-11-27 14:29:20 +00:00
|
|
|
} else if (code == TS_REKEY) {
|
2013-11-17 14:05:41 +00:00
|
|
|
if (!ssh->kex_in_progress && !ssh->bare_connection &&
|
|
|
|
ssh->version == 2) {
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->rekey_reason = "at user request";
|
|
|
|
ssh->rekey_class = RK_NORMAL;
|
2018-05-18 06:22:58 +00:00
|
|
|
queue_idempotent_callback(&ssh->ssh2_transport_icb);
|
2004-11-27 14:29:20 +00:00
|
|
|
}
|
Add manual cross-certification of new host keys.
If a server offers host key algorithms that we don't have a stored key
for, they will now appear in a submenu of the Special Commands menu.
Selecting one will force a repeat key exchange with that key, and if
it succeeds, will add the new host key to the cache. The idea is that
the new key sent by the server is protected by the crypto established
in the previous key exchange, so this is just as safe as typing some
command like 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub' at the
server prompt and transcribing the results manually.
This allows switching over to newer host key algorithms if the client
has begun to support them (e.g. people using PuTTY's new ECC
functionality for the first time), or if the server has acquired a new
key (e.g. due to a server OS upgrade).
At the moment, it's only available manually, for a single host key
type at a time. Automating it is potentially controversial for
security policy reasons (what if someone doesn't agree this is what
they want in their host key cache, or doesn't want to switch over to
using whichever of the keys PuTTY would now put top of the list?), for
code plumbing reasons (chaining several of these rekeys might be more
annoying than doing one at a time) and for CPU usage reasons (rekeys
are expensive), but even so, it might turn out to be a good idea in
future.
2016-03-21 07:25:31 +00:00
|
|
|
} else if (code >= TS_LOCALSTART) {
|
2018-06-03 11:58:05 +00:00
|
|
|
ssh->hostkey_alg = hostkey_algs[code - TS_LOCALSTART].alg;
|
Add manual cross-certification of new host keys.
If a server offers host key algorithms that we don't have a stored key
for, they will now appear in a submenu of the Special Commands menu.
Selecting one will force a repeat key exchange with that key, and if
it succeeds, will add the new host key to the cache. The idea is that
the new key sent by the server is protected by the crypto established
in the previous key exchange, so this is just as safe as typing some
command like 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub' at the
server prompt and transcribing the results manually.
This allows switching over to newer host key algorithms if the client
has begun to support them (e.g. people using PuTTY's new ECC
functionality for the first time), or if the server has acquired a new
key (e.g. due to a server OS upgrade).
At the moment, it's only available manually, for a single host key
type at a time. Automating it is potentially controversial for
security policy reasons (what if someone doesn't agree this is what
they want in their host key cache, or doesn't want to switch over to
using whichever of the keys PuTTY would now put top of the list?), for
code plumbing reasons (chaining several of these rekeys might be more
annoying than doing one at a time) and for CPU usage reasons (rekeys
are expensive), but even so, it might turn out to be a good idea in
future.
2016-03-21 07:25:31 +00:00
|
|
|
ssh->cross_certifying = TRUE;
|
|
|
|
if (!ssh->kex_in_progress && !ssh->bare_connection &&
|
|
|
|
ssh->version == 2) {
|
2018-05-18 06:22:58 +00:00
|
|
|
ssh->rekey_reason = "cross-certifying new host key";
|
|
|
|
ssh->rekey_class = RK_NORMAL;
|
2018-05-18 06:22:58 +00:00
|
|
|
queue_idempotent_callback(&ssh->ssh2_transport_icb);
|
Add manual cross-certification of new host keys.
If a server offers host key algorithms that we don't have a stored key
for, they will now appear in a submenu of the Special Commands menu.
Selecting one will force a repeat key exchange with that key, and if
it succeeds, will add the new host key to the cache. The idea is that
the new key sent by the server is protected by the crypto established
in the previous key exchange, so this is just as safe as typing some
command like 'ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub' at the
server prompt and transcribing the results manually.
This allows switching over to newer host key algorithms if the client
has begun to support them (e.g. people using PuTTY's new ECC
functionality for the first time), or if the server has acquired a new
key (e.g. due to a server OS upgrade).
At the moment, it's only available manually, for a single host key
type at a time. Automating it is potentially controversial for
security policy reasons (what if someone doesn't agree this is what
they want in their host key cache, or doesn't want to switch over to
using whichever of the keys PuTTY would now put top of the list?), for
code plumbing reasons (chaining several of these rekeys might be more
annoying than doing one at a time) and for CPU usage reasons (rekeys
are expensive), but even so, it might turn out to be a good idea in
future.
2016-03-21 07:25:31 +00:00
|
|
|
}
|
2003-04-04 20:21:05 +00:00
|
|
|
} else if (code == TS_BRK) {
|
|
|
|
if (ssh->state == SSH_STATE_CLOSED
|
|
|
|
|| ssh->state == SSH_STATE_PREPACKET) return;
|
|
|
|
if (ssh->version == 1) {
|
2005-03-10 16:36:05 +00:00
|
|
|
logevent("Unable to send BREAK signal in SSH-1");
|
2004-10-13 13:43:11 +00:00
|
|
|
} else if (ssh->mainchan) {
|
2012-08-25 15:57:05 +00:00
|
|
|
pktout = ssh2_chanreq_init(ssh->mainchan, "break", NULL, NULL);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(pktout, 0); /* default break length */
|
2004-11-24 18:45:52 +00:00
|
|
|
ssh2_pkt_send(ssh, pktout);
|
2003-04-04 20:21:05 +00:00
|
|
|
}
|
2000-09-08 15:24:19 +00:00
|
|
|
} else {
|
2004-10-17 21:22:22 +00:00
|
|
|
/* Is is a POSIX signal? */
|
2015-05-15 10:15:42 +00:00
|
|
|
const char *signame = NULL;
|
2004-10-17 21:22:22 +00:00
|
|
|
if (code == TS_SIGABRT) signame = "ABRT";
|
|
|
|
if (code == TS_SIGALRM) signame = "ALRM";
|
|
|
|
if (code == TS_SIGFPE) signame = "FPE";
|
|
|
|
if (code == TS_SIGHUP) signame = "HUP";
|
|
|
|
if (code == TS_SIGILL) signame = "ILL";
|
|
|
|
if (code == TS_SIGINT) signame = "INT";
|
|
|
|
if (code == TS_SIGKILL) signame = "KILL";
|
|
|
|
if (code == TS_SIGPIPE) signame = "PIPE";
|
|
|
|
if (code == TS_SIGQUIT) signame = "QUIT";
|
|
|
|
if (code == TS_SIGSEGV) signame = "SEGV";
|
|
|
|
if (code == TS_SIGTERM) signame = "TERM";
|
|
|
|
if (code == TS_SIGUSR1) signame = "USR1";
|
|
|
|
if (code == TS_SIGUSR2) signame = "USR2";
|
|
|
|
/* The SSH-2 protocol does in principle support arbitrary named
|
|
|
|
* signals, including signame@domain, but we don't support those. */
|
|
|
|
if (signame) {
|
|
|
|
/* It's a signal. */
|
|
|
|
if (ssh->version == 2 && ssh->mainchan) {
|
2012-08-25 15:57:05 +00:00
|
|
|
pktout = ssh2_chanreq_init(ssh->mainchan, "signal", NULL, NULL);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(pktout, signame);
|
2004-11-24 18:45:52 +00:00
|
|
|
ssh2_pkt_send(ssh, pktout);
|
2004-10-17 21:22:22 +00:00
|
|
|
logeventf(ssh, "Sent signal SIG%s", signame);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Never heard of it. Do nothing */
|
|
|
|
}
|
2000-09-08 15:24:19 +00:00
|
|
|
}
|
1999-01-08 13:02:13 +00:00
|
|
|
}
|
|
|
|
|
2018-09-17 11:14:00 +00:00
|
|
|
static unsigned (ssh_alloc_sharing_channel)(
|
|
|
|
ConnectionLayer *cl, ssh_sharing_connstate *connstate)
|
2013-11-17 14:05:41 +00:00
|
|
|
{
|
2018-09-17 11:14:00 +00:00
|
|
|
Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
|
2013-11-17 14:05:41 +00:00
|
|
|
struct ssh_channel *c;
|
|
|
|
c = snew(struct ssh_channel);
|
|
|
|
|
|
|
|
c->ssh = ssh;
|
2016-05-21 21:29:57 +00:00
|
|
|
ssh_channel_init(c);
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
c->chan = NULL;
|
2018-09-13 08:09:10 +00:00
|
|
|
c->sharectx = connstate;
|
2013-11-17 14:05:41 +00:00
|
|
|
return c->localid;
|
|
|
|
}
|
|
|
|
|
2018-09-17 11:14:00 +00:00
|
|
|
static void (ssh_delete_sharing_channel)(ConnectionLayer *cl, unsigned localid)
|
2013-11-17 14:05:41 +00:00
|
|
|
{
|
2018-09-17 11:14:00 +00:00
|
|
|
Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
|
2013-11-17 14:05:41 +00:00
|
|
|
struct ssh_channel *c;
|
|
|
|
|
|
|
|
c = find234(ssh->channels, &localid, ssh_channelfind);
|
|
|
|
if (c)
|
|
|
|
ssh_channel_destroy(c);
|
|
|
|
}
|
|
|
|
|
2018-09-17 11:14:00 +00:00
|
|
|
static void (ssh_send_packet_from_downstream)(
|
|
|
|
ConnectionLayer *cl, unsigned id, int type,
|
|
|
|
const void *data, int datalen, const char *additional_log_text)
|
2013-11-17 14:05:41 +00:00
|
|
|
{
|
2018-09-17 11:14:00 +00:00
|
|
|
Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pkt;
|
2013-11-17 14:05:41 +00:00
|
|
|
|
Move binary packet protocols and censoring out of ssh.c.
sshbpp.h now defines a classoid that encapsulates both directions of
an SSH binary packet protocol - that is, a system for reading a
bufchain of incoming data and turning it into a stream of PktIn, and
another system for taking a PktOut and turning it into data on an
outgoing bufchain.
The state structure in each of those files contains everything that
used to be in the 'rdpkt2_state' structure and its friends, and also
quite a lot of bits and pieces like cipher and MAC states that used to
live in the main Ssh structure.
One minor effect of this layer separation is that I've had to extend
the packet dispatch table by one, because the BPP layer can no longer
directly trigger sending of SSH_MSG_UNIMPLEMENTED for a message too
short to have a type byte. Instead, I extend the PktIn type field to
use an out-of-range value to encode that, and the easiest way to make
that trigger an UNIMPLEMENTED message is to have the dispatch table
contain an entry for it.
(That's a system that may come in useful again - I was also wondering
about inventing a fake type code to indicate network EOF, so that that
could be propagated through the layers and be handled by whichever one
currently knew best how to respond.)
I've also moved the packet-censoring code into its own pair of files,
partly because I was going to want to do that anyway sooner or later,
and mostly because it's called from the BPP code, and the SSH-2
version in particular has to be called from both the main SSH-2 BPP
and the bare unencrypted protocol used for connection sharing. While I
was at it, I took the opportunity to merge the outgoing and incoming
censor functions, so that the parts that were common between them
(e.g. CHANNEL_DATA messages look the same in both directions) didn't
need to be repeated.
2018-06-09 08:09:10 +00:00
|
|
|
pkt = ssh_bpp_new_pktout(ssh->bpp, type);
|
2013-11-17 14:05:41 +00:00
|
|
|
pkt->downstream_id = id;
|
|
|
|
pkt->additional_log_text = additional_log_text;
|
2018-05-24 09:15:36 +00:00
|
|
|
put_data(pkt, data, datalen);
|
2013-11-17 14:05:41 +00:00
|
|
|
ssh2_pkt_send(ssh, pkt);
|
|
|
|
}
|
|
|
|
|
2001-08-25 17:09:23 +00:00
|
|
|
/*
|
|
|
|
* This is called when stdout/stderr (the entity to which
|
|
|
|
* from_backend sends data) manages to clear some backlog.
|
|
|
|
*/
|
2018-09-11 15:23:38 +00:00
|
|
|
static void ssh_unthrottle(Backend *be, int bufsize)
|
2001-08-25 17:09:23 +00:00
|
|
|
{
|
2018-09-11 15:23:38 +00:00
|
|
|
Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
|
2007-10-03 20:29:27 +00:00
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
if (ssh->version == 1) {
|
|
|
|
if (ssh->v1_stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) {
|
|
|
|
ssh->v1_stdout_throttling = 0;
|
2007-10-03 20:29:27 +00:00
|
|
|
ssh_throttle_conn(ssh, -1);
|
2001-08-25 17:09:23 +00:00
|
|
|
}
|
|
|
|
} else {
|
2016-05-17 13:57:51 +00:00
|
|
|
if (ssh->mainchan)
|
|
|
|
ssh_channel_unthrottle(ssh->mainchan, bufsize);
|
2001-08-25 17:09:23 +00:00
|
|
|
}
|
2012-05-12 17:00:54 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now process any SSH connection data that was stashed in our
|
|
|
|
* queue while we were frozen.
|
|
|
|
*/
|
Put all incoming SSH wire data into a bufchain.
I've completely removed the top-level coroutine ssh_gotdata(), and
replaced it with a system in which ssh_receive (which is a plug
function, i.e. called directly from the network code) simply adds the
incoming data to a new bufchain called ssh->incoming_data, and then
queues an idempotent callback to ensure that whatever function is
currently responsible for the top-level handling of wire data will be
invoked in the near future.
So the decisions that ssh_gotdata was previously making are now made
by changing which function is invoked by that idempotent callback:
when we finish doing SSH greeting exchange and move on to the packet-
structured main phase of the protocol, we just change
ssh->current_incoming_data_fn and ensure that the new function gets
called to take over anything still outstanding in the queue.
This simplifies the _other_ end of the API of the rdpkt functions. In
the previous commit, they stopped returning their 'struct Packet'
directly, and instead put it on a queue; in this commit, they're no
longer receiving a (data, length) pair in their parameter list, and
instead, they're just reading from ssh->incoming_data. So now, API-
wise, they take no arguments at all except the main 'ssh' state
structure.
It's not just the rdpkt functions that needed to change, of course.
The SSH greeting handlers have also had to switch to reading from
ssh->incoming_data, and are quite substantially rewritten as a result.
(I think they look simpler in the new style, personally.)
This new bufchain takes over from the previous queued_incoming_data,
which was only used at all in cases where we throttled the entire SSH
connection. Now, data is unconditionally left on the new bufchain
whether we're throttled or not, and the only question is whether we're
currently bothering to read it; so all the special-purpose code to
read data from a bufchain and pass it to rdpkt can go away, because
rdpkt itself already knows how to do that job.
One slightly fiddly point is that we now have to defer processing of
EOF from the SSH server: if we have data already in the incoming
bufchain and then the server slams the connection shut, we want to
process the data we've got _before_ reacting to the remote EOF, just
in case that data gives us some reason to change our mind about how we
react to the EOF, or a last-minute important piece of data we might
need to log.
2018-05-18 06:22:57 +00:00
|
|
|
queue_idempotent_callback(&ssh->incoming_data_consumer);
|
2001-08-25 17:09:23 +00:00
|
|
|
}
|
|
|
|
|
2018-09-17 11:14:00 +00:00
|
|
|
static SshChannel *(ssh_lportfwd_open)(
|
|
|
|
ConnectionLayer *cl, const char *hostname, int port,
|
|
|
|
const char *org, Channel *chan)
|
2001-08-08 20:44:35 +00:00
|
|
|
{
|
2018-09-17 11:14:00 +00:00
|
|
|
Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl);
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
struct ssh_channel *c = snew(struct ssh_channel);
|
Separate Packet into two structures.
This is the first stage of massively tidying up this very confused
data structure. In this commit, I replace the unified 'struct Packet'
with two structures PktIn and PktOut, each of which contains only the
fields of struct Packet that are actually used for packets going in
that direction - most notably, PktIn doesn't implement BinarySink, and
PktOut doesn't implement BinarySource.
All uses of the old structure were statically determinable to be one
or the other, so I've done that determination and changed all the
types of variables and function signatures.
Unlike PktIn, PktOut is not reference-counted, so there's a new
ssh_pktout_free function.
The most immediately pleasing thing about this change is that it lets
me finally get rid of the tedious comment explaining how the 'length'
field in struct Packet meant something different depending on
direction. Now it's two fields of the same name in two different
structures, I can comment the same thing much less verbosely!
(I've also got rid of the comment claiming the type field was only
used for incoming packets. That wasn't even true! It might have been
once, because you can write an outgoing packet's type byte straight
into its data buffer, but in fact in the current code pktout->type is
nonetheless used in various other places, e.g. log_outgoing_packet.)
In this commit I've only removed the fields from each structure that
were _already_ unused. There are still quite a few we can get rid of
by _making_ them unused.
2018-06-05 07:20:10 +00:00
|
|
|
PktOut *pktout;
|
2001-08-08 20:44:35 +00:00
|
|
|
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
c->ssh = ssh;
|
|
|
|
ssh_channel_init(c);
|
|
|
|
c->halfopen = TRUE;
|
|
|
|
c->chan = chan;
|
|
|
|
|
2012-09-08 19:46:07 +00:00
|
|
|
logeventf(ssh, "Opening connection to %s:%d for %s", hostname, port, org);
|
2001-08-08 20:44:35 +00:00
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
if (ssh->version == 1) {
|
2018-06-09 08:59:58 +00:00
|
|
|
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH1_MSG_PORT_OPEN);
|
|
|
|
put_uint32(pktout, c->localid);
|
|
|
|
put_stringz(pktout, hostname);
|
|
|
|
put_uint32(pktout, port);
|
|
|
|
/* originator string would go here, but we didn't specify
|
|
|
|
* SSH_PROTOFLAG_HOST_IN_FWD_OPEN */
|
|
|
|
ssh_pkt_write(ssh, pktout);
|
2001-08-09 21:17:05 +00:00
|
|
|
} else {
|
2012-09-01 12:28:38 +00:00
|
|
|
pktout = ssh2_chanopen_init(c, "direct-tcpip");
|
2014-01-25 15:58:54 +00:00
|
|
|
{
|
|
|
|
char *trimmed_host = host_strduptrim(hostname);
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(pktout, trimmed_host);
|
2014-01-25 15:58:54 +00:00
|
|
|
sfree(trimmed_host);
|
|
|
|
}
|
2018-05-24 09:15:36 +00:00
|
|
|
put_uint32(pktout, port);
|
2001-08-09 21:17:05 +00:00
|
|
|
/*
|
|
|
|
* We make up values for the originator data; partly it's
|
|
|
|
* too much hassle to keep track, and partly I'm not
|
|
|
|
* convinced the server should be told details like that
|
|
|
|
* about my local network configuration.
|
2005-12-07 18:01:40 +00:00
|
|
|
* The "originator IP address" is syntactically a numeric
|
|
|
|
* IP address, and some servers (e.g., Tectia) get upset
|
|
|
|
* if it doesn't match this syntax.
|
2001-08-09 21:17:05 +00:00
|
|
|
*/
|
2018-05-24 09:15:36 +00:00
|
|
|
put_stringz(pktout, "0.0.0.0");
|
|
|
|
put_uint32(pktout, 0);
|
2004-11-24 18:45:52 +00:00
|
|
|
ssh2_pkt_send(ssh, pktout);
|
2001-08-09 21:17:05 +00:00
|
|
|
}
|
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local
side of an SSH connection-layer channel, in terms of knowing where to
send incoming channel data to, whether to close the channel, etc.
Channel and the previous 'struct ssh_channel' mutually refer. The
latter contains all the SSH-specific parts, and as much of the common
logic as possible: in particular, Channel doesn't have to know
anything about SSH packet formats, or which SSH protocol version is in
use, or deal with all the fiddly stuff about window sizes - with the
exception that x11fwd.c's implementation of it does have to be able to
ask for a small fixed initial window size for the bodgy system that
distinguishes upstream from downstream X forwardings.
I've taken the opportunity to move the code implementing the detailed
behaviour of agent forwarding out of ssh.c, now that all of it is on
the far side of a uniform interface. (This also means that if I later
implement agent forwarding directly to a Unix socket as an
alternative, it'll be a matter of changing just the one call to
agentf_new() that makes the Channel to plug into a forwarding.)
2018-09-12 14:03:47 +00:00
|
|
|
|
2018-09-14 12:47:13 +00:00
|
|
|
return &c->sc;
|
2001-08-08 20:44:35 +00:00
|
|
|
}
|
|
|
|
|
2018-09-11 15:23:38 +00:00
|
|
|
static int ssh_connected(Backend *be)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2018-09-11 15:23:38 +00:00
|
|
|
Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
|
2006-08-27 08:03:19 +00:00
|
|
|
return ssh->s != NULL;
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
2000-09-08 16:42:11 +00:00
|
|
|
|
2018-09-11 15:23:38 +00:00
|
|
|
static int ssh_sendok(Backend *be)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2018-09-11 15:23:38 +00:00
|
|
|
Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
|
2002-10-25 11:30:33 +00:00
|
|
|
return ssh->send_ok;
|
2001-05-06 14:35:20 +00:00
|
|
|
}
|
2000-06-22 12:18:34 +00:00
|
|
|
|
2018-09-11 15:23:38 +00:00
|
|
|
static int ssh_ldisc(Backend *be, int option)
|
2001-05-06 14:35:20 +00:00
|
|
|
{
|
2018-09-11 15:23:38 +00:00
|
|
|
Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
|
2001-05-06 14:35:20 +00:00
|
|
|
if (option == LD_ECHO)
|
2002-10-25 11:30:33 +00:00
|
|
|
return ssh->echoing;
|
2001-05-06 14:35:20 +00:00
|
|
|
if (option == LD_EDIT)
|
2002-10-25 11:30:33 +00:00
|
|
|
return ssh->editing;
|
2001-01-24 14:08:20 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2018-09-11 15:23:38 +00:00
|
|
|
static void ssh_provide_ldisc(Backend *be, Ldisc *ldisc)
|
2002-10-26 10:16:19 +00:00
|
|
|
{
|
2018-09-11 15:23:38 +00:00
|
|
|
Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
|
2002-10-26 10:16:19 +00:00
|
|
|
ssh->ldisc = ldisc;
|
|
|
|
}
|
|
|
|
|
2018-09-11 15:23:38 +00:00
|
|
|
static void ssh_provide_logctx(Backend *be, LogContext *logctx)
|
2002-10-26 12:58:13 +00:00
|
|
|
{
|
2018-09-11 15:23:38 +00:00
|
|
|
Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
|
2002-10-26 12:58:13 +00:00
|
|
|
ssh->logctx = logctx;
|
|
|
|
}
|
|
|
|
|
2018-09-11 15:23:38 +00:00
|
|
|
static int ssh_return_exitcode(Backend *be)
|
2002-10-25 11:30:33 +00:00
|
|
|
{
|
2018-09-11 15:23:38 +00:00
|
|
|
Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
|
2003-03-31 13:15:51 +00:00
|
|
|
if (ssh->s != NULL)
|
|
|
|
return -1;
|
|
|
|
else
|
2006-08-26 09:21:52 +00:00
|
|
|
return (ssh->exitcode >= 0 ? ssh->exitcode : INT_MAX);
|
2002-10-25 11:30:33 +00:00
|
|
|
}
|
|
|
|
|
2004-12-29 12:32:25 +00:00
|
|
|
/*
|
2014-11-09 00:10:46 +00:00
|
|
|
* cfg_info for SSH is the protocol running in this session.
|
|
|
|
* (1 or 2 for the full SSH-1 or SSH-2 protocol; -1 for the bare
|
|
|
|
* SSH-2 connection protocol, i.e. a downstream; 0 for not-decided-yet.)
|
2004-12-29 12:32:25 +00:00
|
|
|
*/
|
2018-09-11 15:23:38 +00:00
|
|
|
static int ssh_cfg_info(Backend *be)
|
2004-12-29 12:32:25 +00:00
|
|
|
{
|
2018-09-11 15:23:38 +00:00
|
|
|
Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
|
2014-11-09 00:10:46 +00:00
|
|
|
if (ssh->version == 0)
|
|
|
|
return 0; /* don't know yet */
|
|
|
|
else if (ssh->bare_connection)
|
|
|
|
return -1;
|
|
|
|
else
|
|
|
|
return ssh->version;
|
2004-12-29 12:32:25 +00:00
|
|
|
}
|
|
|
|
|
2002-10-25 11:30:33 +00:00
|
|
|
/*
|
|
|
|
* Gross hack: pscp will try to start SFTP but fall back to scp1 if
|
|
|
|
* that fails. This variable is the means by which scp.c can reach
|
|
|
|
* into the SSH code and find out which one it got.
|
|
|
|
*/
|
2018-09-11 15:23:38 +00:00
|
|
|
extern int ssh_fallback_cmd(Backend *be)
|
2001-12-29 15:31:42 +00:00
|
|
|
{
|
2018-09-11 15:23:38 +00:00
|
|
|
Ssh ssh = FROMFIELD(be, struct ssh_tag, backend);
|
2002-10-25 11:30:33 +00:00
|
|
|
return ssh->fallback_cmd;
|
2001-12-29 15:31:42 +00:00
|
|
|
}
|
|
|
|
|
2018-09-11 15:23:38 +00:00
|
|
|
const struct Backend_vtable ssh_backend = {
|
1999-01-08 13:02:13 +00:00
|
|
|
ssh_init,
|
2003-01-15 23:30:21 +00:00
|
|
|
ssh_free,
|
2003-01-12 14:48:29 +00:00
|
|
|
ssh_reconfig,
|
1999-01-08 13:02:13 +00:00
|
|
|
ssh_send,
|
2001-08-25 17:09:23 +00:00
|
|
|
ssh_sendbuffer,
|
1999-01-08 13:02:13 +00:00
|
|
|
ssh_size,
|
2000-09-08 14:45:20 +00:00
|
|
|
ssh_special,
|
2003-04-04 20:21:05 +00:00
|
|
|
ssh_get_specials,
|
2006-08-27 08:03:19 +00:00
|
|
|
ssh_connected,
|
2001-12-29 15:31:42 +00:00
|
|
|
ssh_return_exitcode,
|
2000-10-04 14:35:15 +00:00
|
|
|
ssh_sendok,
|
2001-01-24 14:08:20 +00:00
|
|
|
ssh_ldisc,
|
2002-10-26 10:16:19 +00:00
|
|
|
ssh_provide_ldisc,
|
2002-10-26 12:58:13 +00:00
|
|
|
ssh_provide_logctx,
|
2001-08-25 17:09:23 +00:00
|
|
|
ssh_unthrottle,
|
2004-12-29 12:32:25 +00:00
|
|
|
ssh_cfg_info,
|
2015-09-25 10:46:28 +00:00
|
|
|
ssh_test_for_upstream,
|
2007-06-30 21:56:44 +00:00
|
|
|
"ssh",
|
|
|
|
PROT_SSH,
|
2000-10-04 14:35:15 +00:00
|
|
|
22
|
2001-08-09 21:17:05 +00:00
|
|
|
};
|