1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00
putty-source/ssh2transport.h
Simon Tatham 1d323d5c80 Add an actual SSH server program.
This server is NOT SECURE! If anyone is reading this commit message,
DO NOT DEPLOY IT IN A HOSTILE-FACING ENVIRONMENT! Its purpose is to
speak the server end of everything PuTTY speaks on the client side, so
that I can test that I haven't broken PuTTY when I reorganise its
code, even things like RSA key exchange or chained auth methods which
it's hard to find a server that speaks at all.

(For this reason, it's declared with [UT] in the Recipe file, so that
it falls into the same category as programs like testbn, which won't
be installed by 'make install'.)

Working title is 'Uppity', partly for 'Universal PuTTY Protocol
Interaction Test Yoke', but mostly because it looks quite like the
word 'PuTTY' with part of it reversed. (Apparently 'test yoke' is a
very rarely used term meaning something not altogether unlike 'test
harness', which is a bit of a stretch, but it'll do.)

It doesn't actually _support_ everything I want yet. At the moment,
it's a proof of concept only. But it has most of the machinery
present, and the parts it's missing - such as chained auth methods -
should be easy enough to add because I've built in the required
flexibility, in the form of an AuthPolicy object which can request
them if it wants to. However, the current AuthPolicy object is
entirely trivial, and will let in any user with the password "weasel".

(Another way in which this is not a production-ready server is that it
also has no interaction with the OS's authentication system. In
particular, it will not only let in any user with the same password,
but it won't even change uid - it will open shells and forwardings
under whatever user id you started it up as.)

Currently, the program can only speak the SSH protocol on its standard
I/O channels (using the new FdSocket facility), so if you want it to
listen on a network port, you'll have to run it from some kind of
separate listening program similar to inetd. For my own tests, I'm not
even doing that: I'm just having PuTTY spawn it as a local proxy
process, which also conveniently eliminates the risk of anyone hostile
connecting to it.

The bulk of the actual code reorganisation is already done by previous
commits, so this change is _mostly_ just dropping in a new set of
server-specific source files alongside the client-specific ones I
created recently. The remaining changes in the shared SSH code are
numerous, but all minor:

 - a few extra parameters to BPP and PPL constructors (e.g. 'are you
   in server mode?'), and pass both sets of SSH-1 protocol flags from
   the login to the connection layer
 - in server mode, unconditionally send our version string _before_
   waiting for the remote one
 - a new hook in the SSH-1 BPP to handle enabling compression in
   server mode, where the message exchange works the other way round
 - new code in the SSH-2 BPP to do _deferred_ compression the other
   way round (the non-deferred version is still nicely symmetric)
 - in the SSH-2 transport layer, some adjustments to do key derivation
   either way round (swapping round the identifying letters in the
   various hash preimages, and making sure to list the KEXINITs in the
   right order)
 - also in the SSH-2 transport layer, an if statement that controls
   whether we send SERVICE_REQUEST and wait for SERVICE_ACCEPT, or
   vice versa
 - new ConnectionLayer methods for opening outgoing channels for X and
   agent forwardings
 - new functions in portfwd.c to establish listening sockets suitable
   for remote-to-local port forwarding (i.e. not under the direction
   of a Conf the way it's done on the client side).
2018-10-21 10:02:10 +01:00

227 lines
7.1 KiB
C

/*
* Header connecting the pieces of the SSH-2 transport layer.
*/
#ifndef PUTTY_SSH2TRANSPORT_H
#define PUTTY_SSH2TRANSPORT_H
#ifndef NO_GSSAPI
#include "sshgssc.h"
#include "sshgss.h"
#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 */
#endif
#define DH_MIN_SIZE 1024
#define DH_MAX_SIZE 8192
enum kexlist {
KEXLIST_KEX, KEXLIST_HOSTKEY, KEXLIST_CSCIPHER, KEXLIST_SCCIPHER,
KEXLIST_CSMAC, KEXLIST_SCMAC, KEXLIST_CSCOMP, KEXLIST_SCCOMP,
NKEXLIST
};
#define MAXKEXLIST 16
struct kexinit_algorithm {
const char *name;
union {
struct {
const struct ssh_kex *kex;
int warn;
} kex;
struct {
const ssh_keyalg *hostkey;
int warn;
} hk;
struct {
const struct ssh2_cipheralg *cipher;
int warn;
} cipher;
struct {
const struct ssh2_macalg *mac;
int etm;
} mac;
struct {
const struct ssh_compression_alg *comp;
int delayed;
} comp;
} u;
};
#define HOSTKEY_ALGORITHMS(X) \
X(HK_ED25519, ssh_ecdsa_ed25519) \
X(HK_ECDSA, ssh_ecdsa_nistp256) \
X(HK_ECDSA, ssh_ecdsa_nistp384) \
X(HK_ECDSA, ssh_ecdsa_nistp521) \
X(HK_DSA, ssh_dss) \
X(HK_RSA, ssh_rsa) \
/* end of list */
#define COUNT_HOSTKEY_ALGORITHM(type, alg) +1
#define N_HOSTKEY_ALGORITHMS (0 HOSTKEY_ALGORITHMS(COUNT_HOSTKEY_ALGORITHM))
struct ssh_signkey_with_user_pref_id {
const ssh_keyalg *alg;
int id;
};
extern const struct ssh_signkey_with_user_pref_id
ssh2_hostkey_algs[N_HOSTKEY_ALGORITHMS];
/*
* Enumeration of high-level classes of reason why we might need to do
* a repeat key exchange. A full detailed reason in human-readable
* string form for the Event Log is also provided, but this enum type
* is used to discriminate between classes of reason that the code
* needs to treat differently.
*
* 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_SERVER indicates that we're rekeying
* because the server asked for it, not because we decided it
* ourselves. RK_NORMAL is the usual case. 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 the user-readable reason text is passed in to the
* transport layer as NULL, whereas fills in the reason text after it
* decides whether it needs a rekey at all. In the other cases,
* rekey_reason is passed in to the at the same time as rekey_class.
*/
typedef enum RekeyClass {
RK_NONE = 0,
RK_INITIAL,
RK_SERVER,
RK_NORMAL,
RK_POST_USERAUTH,
RK_GSS_UPDATE
} RekeyClass;
typedef struct transport_direction {
const struct ssh2_cipheralg *cipher;
const struct ssh2_macalg *mac;
int etm_mode;
const struct ssh_compression_alg *comp;
int comp_delayed;
int mkkey_adjust;
} transport_direction;
struct ssh2_transport_state {
int crState, crStateKex;
PacketProtocolLayer *higher_layer;
PktInQueue pq_in_higher;
PktOutQueue pq_out_higher;
IdempotentCallback ic_pq_out_higher;
Conf *conf;
char *savedhost;
int savedport;
const char *rekey_reason;
enum RekeyClass rekey_class;
unsigned long max_data_size;
const struct ssh_kex *kex_alg;
const ssh_keyalg *hostkey_alg;
char *hostkey_str; /* string representation, for easy checking in rekeys */
unsigned char session_id[SSH2_KEX_MAX_HASH_LEN];
int session_id_len;
int dh_min_size, dh_max_size, dh_got_size_bounds;
struct dh_ctx *dh_ctx;
ssh_hash *exhash;
struct DataTransferStats *stats;
char *client_greeting, *server_greeting;
int kex_in_progress;
unsigned long next_rekey, last_rekey;
const char *deferred_rekey_reason;
int higher_layer_ok;
/*
* Fully qualified host name, which we need if doing GSSAPI.
*/
char *fullhostname;
/* shgss is outside the ifdef on purpose to keep APIs simple. If
* NO_GSSAPI is not defined, then it's just an opaque structure
* tag and the pointer will be NULL. */
struct ssh_connection_shared_gss_state *shgss;
#ifndef NO_GSSAPI
int gss_status;
time_t gss_cred_expiry; /* Re-delegate if newer */
unsigned long gss_ctxt_lifetime; /* Re-delegate when short */
#endif
ssh_transient_hostkey_cache *thc;
int gss_kex_used;
int nbits, pbits, warn_kex, warn_hk, warn_cscipher, warn_sccipher;
Bignum p, g, e, f, K;
strbuf *outgoing_kexinit, *incoming_kexinit;
strbuf *client_kexinit, *server_kexinit; /* aliases to the above */
int kex_init_value, kex_reply_value;
transport_direction in, out;
ptrlen hostkeydata, sigdata;
strbuf *hostkeyblob;
char *keystr, *fingerprint;
ssh_key *hkey; /* actual host key */
struct RSAKey *rsa_kex_key; /* for RSA kex */
struct ec_key *ecdh_key; /* for ECDH kex */
unsigned char exchange_hash[SSH2_KEX_MAX_HASH_LEN];
int can_gssapi_keyex;
int need_gss_transient_hostkey;
int warned_about_no_gss_transient_hostkey;
int got_session_id;
int dlgret;
int guessok;
int ignorepkt;
struct kexinit_algorithm kexlists[NKEXLIST][MAXKEXLIST];
#ifndef NO_GSSAPI
Ssh_gss_buf gss_buf;
Ssh_gss_buf gss_rcvtok, gss_sndtok;
Ssh_gss_stat gss_stat;
Ssh_gss_buf mic;
int init_token_sent;
int complete_rcvd;
int gss_delegate;
#endif
/*
* 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[N_HOSTKEY_ALGORITHMS];
int n_uncert_hostkeys;
/*
* Flag indicating that the current rekey is intended to finish
* with a newly cross-certified host key.
*/
int cross_certifying;
ssh_key *const *hostkeys;
int nhostkeys;
PacketProtocolLayer ppl;
};
/* Helpers shared between transport and kex */
PktIn *ssh2_transport_pop(struct ssh2_transport_state *s);
void ssh2_transport_dialog_callback(void *, int);
/* Provided by transport for use in kex */
void ssh2transport_finalise_exhash(struct ssh2_transport_state *s);
/* Provided by kex for use in transport */
void ssh2kex_coroutine(struct ssh2_transport_state *s);
#endif /* PUTTY_SSH2TRANSPORT_H */