mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
2ca0070f89
I've tried to separate out as many individually coherent changes from
this work as I could into their own commits, but here's where I run
out and have to commit the rest of this major refactoring as a
big-bang change.
Most of ssh.c is now no longer in ssh.c: all five of the main
coroutines that handle layers of the SSH-1 and SSH-2 protocols now
each have their own source file to live in, and a lot of the
supporting functions have moved into the appropriate one of those too.
The new abstraction is a vtable called 'PacketProtocolLayer', which
has an input and output packet queue. Each layer's main coroutine is
invoked from the method ssh_ppl_process_queue(), which is usually
(though not exclusively) triggered automatically when things are
pushed on the input queue. In SSH-2, the base layer is the transport
protocol, and it contains a pair of subsidiary queues by which it
passes some of its packets to the higher SSH-2 layers - first userauth
and then connection, which are peers at the same level, with the
former abdicating in favour of the latter at the appropriate moment.
SSH-1 is simpler: the whole login phase of the protocol (crypto setup
and authentication) is all in one module, and since SSH-1 has no
repeat key exchange, that setup layer abdicates in favour of the
connection phase when it's done.
ssh.c itself is now about a tenth of its old size (which all by itself
is cause for celebration!). Its main job is to set up all the layers,
hook them up to each other and to the BPP, and to funnel data back and
forth between that collection of modules and external things such as
the network and the terminal. Once it's set up a collection of packet
protocol layers, it communicates with them partly by calling methods
of the base layer (and if that's ssh2transport then it will delegate
some functionality to the corresponding methods of its higher layer),
and partly by talking directly to the connection layer no matter where
it is in the stack by means of the separate ConnectionLayer vtable
which I introduced in commit 8001dd4cb
, and to which I've now added
quite a few extra methods replacing services that used to be internal
function calls within ssh.c.
(One effect of this is that the SSH-1 and SSH-2 channel storage is now
no longer shared - there are distinct struct types ssh1_channel and
ssh2_channel. That means a bit more code duplication, but on the plus
side, a lot fewer confusing conditionals in the middle of half-shared
functions, and less risk of a piece of SSH-1 escaping into SSH-2 or
vice versa, which I remember has happened at least once in the past.)
The bulk of this commit introduces the five new source files, their
common header sshppl.h and some shared supporting routines in
sshcommon.c, and rewrites nearly all of ssh.c itself. But it also
includes a couple of other changes that I couldn't separate easily
enough:
Firstly, there's a new handling for socket EOF, in which ssh.c sets an
'input_eof' flag in the BPP, and that responds by checking a flag that
tells it whether to report the EOF as an error or not. (This is the
main reason for those new BPP_READ / BPP_WAITFOR macros - they can
check the EOF flag every time the coroutine is resumed.)
Secondly, the error reporting itself is changed around again. I'd
expected to put some data fields in the public PacketProtocolLayer
structure that it could set to report errors in the same way as the
BPPs have been doing, but in the end, I decided propagating all those
data fields around was a pain and that even the BPPs shouldn't have
been doing it that way. So I've reverted to a system where everything
calls back to functions in ssh.c itself to report any connection-
ending condition. But there's a new family of those functions,
categorising the possible such conditions by semantics, and each one
has a different set of detailed effects (e.g. how rudely to close the
network connection, what exit status should be passed back to the
whole application, whether to send a disconnect message and/or display
a GUI error box).
I don't expect this to be immediately perfect: of course, the code has
been through a big upheaval, new bugs are expected, and I haven't been
able to do a full job of testing (e.g. I haven't tested every auth or
kex method). But I've checked that it _basically_ works - both SSH
protocols, all the different kinds of forwarding channel, more than
one auth method, Windows and Linux, connection sharing - and I think
it's now at the point where the easiest way to find further bugs is to
let it out into the wild and see what users can spot.
218 lines
6.5 KiB
C
218 lines
6.5 KiB
C
#ifndef PUTTY_SSHGSS_H
|
|
#define PUTTY_SSHGSS_H
|
|
#include "putty.h"
|
|
#include "pgssapi.h"
|
|
|
|
#ifndef NO_GSSAPI
|
|
|
|
#define SSH2_GSS_OIDTYPE 0x06
|
|
typedef void *Ssh_gss_ctx;
|
|
|
|
typedef enum Ssh_gss_stat {
|
|
SSH_GSS_OK = 0,
|
|
SSH_GSS_S_CONTINUE_NEEDED,
|
|
SSH_GSS_NO_MEM,
|
|
SSH_GSS_BAD_HOST_NAME,
|
|
SSH_GSS_BAD_MIC,
|
|
SSH_GSS_NO_CREDS,
|
|
SSH_GSS_FAILURE
|
|
} Ssh_gss_stat;
|
|
|
|
#define SSH_GSS_S_COMPLETE SSH_GSS_OK
|
|
|
|
#define SSH_GSS_CLEAR_BUF(buf) do { \
|
|
(*buf).length = 0; \
|
|
(*buf).value = NULL; \
|
|
} while (0)
|
|
|
|
typedef gss_buffer_desc Ssh_gss_buf;
|
|
typedef gss_name_t Ssh_gss_name;
|
|
|
|
#define GSS_NO_EXPIRATION ((time_t)-1)
|
|
|
|
#define GSS_DEF_REKEY_MINS 2 /* Default minutes between GSS cache checks */
|
|
|
|
/* Functions, provided by either wingss.c or sshgssc.c */
|
|
|
|
struct ssh_gss_library;
|
|
|
|
/*
|
|
* Prepare a collection of GSSAPI libraries for use in a single SSH
|
|
* connection. Returns a structure containing a list of libraries,
|
|
* with their ids (see struct ssh_gss_library below) filled in so
|
|
* that the client can go through them in the SSH user's preferred
|
|
* order.
|
|
*
|
|
* Must always return non-NULL. (Even if no libraries are available,
|
|
* it must return an empty structure.)
|
|
*
|
|
* The free function cleans up the structure, and its associated
|
|
* libraries (if any).
|
|
*/
|
|
struct ssh_gss_liblist {
|
|
struct ssh_gss_library *libraries;
|
|
int nlibraries;
|
|
};
|
|
struct ssh_gss_liblist *ssh_gss_setup(Conf *conf);
|
|
void ssh_gss_cleanup(struct ssh_gss_liblist *list);
|
|
|
|
/*
|
|
* Fills in buf with a string describing the GSSAPI mechanism in
|
|
* use. buf->data is not dynamically allocated.
|
|
*/
|
|
typedef Ssh_gss_stat (*t_ssh_gss_indicate_mech)(struct ssh_gss_library *lib,
|
|
Ssh_gss_buf *buf);
|
|
|
|
/*
|
|
* Converts a name such as a hostname into a GSSAPI internal form,
|
|
* which is placed in "out". The result should be freed by
|
|
* ssh_gss_release_name().
|
|
*/
|
|
typedef Ssh_gss_stat (*t_ssh_gss_import_name)(struct ssh_gss_library *lib,
|
|
char *in, Ssh_gss_name *out);
|
|
|
|
/*
|
|
* Frees the contents of an Ssh_gss_name structure filled in by
|
|
* ssh_gss_import_name().
|
|
*/
|
|
typedef Ssh_gss_stat (*t_ssh_gss_release_name)(struct ssh_gss_library *lib,
|
|
Ssh_gss_name *name);
|
|
|
|
/*
|
|
* The main GSSAPI security context setup function. The "out"
|
|
* parameter will need to be freed by ssh_gss_free_tok.
|
|
*/
|
|
typedef Ssh_gss_stat (*t_ssh_gss_init_sec_context)
|
|
(struct ssh_gss_library *lib,
|
|
Ssh_gss_ctx *ctx, Ssh_gss_name name, int delegate,
|
|
Ssh_gss_buf *in, Ssh_gss_buf *out, time_t *expiry,
|
|
unsigned long *lifetime);
|
|
|
|
/*
|
|
* Frees the contents of an Ssh_gss_buf filled in by
|
|
* ssh_gss_init_sec_context(). Do not accidentally call this on
|
|
* something filled in by ssh_gss_get_mic() (which requires a
|
|
* different free function) or something filled in by any other
|
|
* way.
|
|
*/
|
|
typedef Ssh_gss_stat (*t_ssh_gss_free_tok)(struct ssh_gss_library *lib,
|
|
Ssh_gss_buf *);
|
|
|
|
/*
|
|
* Acquires the credentials to perform authentication in the first
|
|
* place. Needs to be freed by ssh_gss_release_cred().
|
|
*/
|
|
typedef Ssh_gss_stat (*t_ssh_gss_acquire_cred)(struct ssh_gss_library *lib,
|
|
Ssh_gss_ctx *,
|
|
time_t *expiry);
|
|
|
|
/*
|
|
* Frees the contents of an Ssh_gss_ctx filled in by
|
|
* ssh_gss_acquire_cred().
|
|
*/
|
|
typedef Ssh_gss_stat (*t_ssh_gss_release_cred)(struct ssh_gss_library *lib,
|
|
Ssh_gss_ctx *);
|
|
|
|
/*
|
|
* Gets a MIC for some input data. "out" needs to be freed by
|
|
* ssh_gss_free_mic().
|
|
*/
|
|
typedef Ssh_gss_stat (*t_ssh_gss_get_mic)(struct ssh_gss_library *lib,
|
|
Ssh_gss_ctx ctx, Ssh_gss_buf *in,
|
|
Ssh_gss_buf *out);
|
|
|
|
/*
|
|
* Validates an input MIC for some input data.
|
|
*/
|
|
typedef Ssh_gss_stat (*t_ssh_gss_verify_mic)(struct ssh_gss_library *lib,
|
|
Ssh_gss_ctx ctx,
|
|
Ssh_gss_buf *in_data,
|
|
Ssh_gss_buf *in_mic);
|
|
|
|
/*
|
|
* Frees the contents of an Ssh_gss_buf filled in by
|
|
* ssh_gss_get_mic(). Do not accidentally call this on something
|
|
* filled in by ssh_gss_init_sec_context() (which requires a
|
|
* different free function) or something filled in by any other
|
|
* way.
|
|
*/
|
|
typedef Ssh_gss_stat (*t_ssh_gss_free_mic)(struct ssh_gss_library *lib,
|
|
Ssh_gss_buf *);
|
|
|
|
/*
|
|
* Return an error message after authentication failed. The
|
|
* message string is returned in "buf", with buf->len giving the
|
|
* number of characters of printable message text and buf->data
|
|
* containing one more character which is a trailing NUL.
|
|
* buf->data should be manually freed by the caller.
|
|
*/
|
|
typedef Ssh_gss_stat (*t_ssh_gss_display_status)(struct ssh_gss_library *lib,
|
|
Ssh_gss_ctx, Ssh_gss_buf *buf);
|
|
|
|
struct ssh_gss_library {
|
|
/*
|
|
* Identifying number in the enumeration used by the
|
|
* configuration code to specify a preference order.
|
|
*/
|
|
int id;
|
|
|
|
/*
|
|
* Filled in at initialisation time, if there's anything
|
|
* interesting to say about how GSSAPI was initialised (e.g.
|
|
* which of a number of alternative libraries was used).
|
|
*/
|
|
const char *gsslogmsg;
|
|
|
|
/*
|
|
* Function pointers implementing the SSH wrapper layer on top
|
|
* of GSSAPI. (Defined in sshgssc, typically, though Windows
|
|
* provides an alternative layer to sit on top of the annoyingly
|
|
* different SSPI.)
|
|
*/
|
|
t_ssh_gss_indicate_mech indicate_mech;
|
|
t_ssh_gss_import_name import_name;
|
|
t_ssh_gss_release_name release_name;
|
|
t_ssh_gss_init_sec_context init_sec_context;
|
|
t_ssh_gss_free_tok free_tok;
|
|
t_ssh_gss_acquire_cred acquire_cred;
|
|
t_ssh_gss_release_cred release_cred;
|
|
t_ssh_gss_get_mic get_mic;
|
|
t_ssh_gss_verify_mic verify_mic;
|
|
t_ssh_gss_free_mic free_mic;
|
|
t_ssh_gss_display_status display_status;
|
|
|
|
/*
|
|
* Additional data for the wrapper layers.
|
|
*/
|
|
union {
|
|
struct gssapi_functions gssapi;
|
|
/*
|
|
* The SSPI wrappers don't need to store their Windows API
|
|
* function pointers in this structure, because there can't
|
|
* be more than one set of them available.
|
|
*/
|
|
} u;
|
|
|
|
/*
|
|
* Wrapper layers will often also need to store a library handle
|
|
* of some sort for cleanup time.
|
|
*/
|
|
void *handle;
|
|
};
|
|
|
|
/*
|
|
* State that has to be shared between all GSSAPI-using parts of the
|
|
* same SSH connection, in particular between GSS key exchange and the
|
|
* subsequent trivial userauth method that reuses its output.
|
|
*/
|
|
struct ssh_connection_shared_gss_state {
|
|
struct ssh_gss_liblist *libs;
|
|
struct ssh_gss_library *lib;
|
|
Ssh_gss_name srv_name;
|
|
Ssh_gss_ctx ctx;
|
|
};
|
|
|
|
#endif /* NO_GSSAPI */
|
|
|
|
#endif /*PUTTY_SSHGSS_H*/
|