1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 09:27:59 +00:00

Move most of ssh.c out into separate source files.

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.
This commit is contained in:
Simon Tatham 2018-09-24 18:28:16 +01:00
parent 344ec3aec5
commit 2ca0070f89
17 changed files with 10421 additions and 9210 deletions

1
Recipe
View File

@ -251,6 +251,7 @@ NONSSH = telnet raw rlogin ldisc pinger
# SSH back end (putty, plink, pscp, psftp).
SSH = ssh sshcommon ssh1bpp ssh2bpp ssh2bpp-bare ssh1censor ssh2censor
+ ssh1login ssh1connection ssh2transport ssh2userauth ssh2connection
+ sshverstring sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf
+ sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd
+ sshaes sshccp sshsh256 sshsh512 sshbn wildcard pinger ssharcf

1
defs.h
View File

@ -95,6 +95,7 @@ typedef struct ptrlen {
typedef struct logblank_t logblank_t;
typedef struct BinaryPacketProtocol BinaryPacketProtocol;
typedef struct PacketProtocolLayer PacketProtocolLayer;
/* Do a compile-time type-check of 'to_check' (without evaluating it),
* as a side effect of returning the value 'to_return'. Note that

9632
ssh.c

File diff suppressed because it is too large Load Diff

56
ssh.h
View File

@ -183,7 +183,18 @@ void share_setup_x11_channel(ssh_sharing_connstate *cs, share_channel *chan,
int protomajor, int protominor,
const void *initial_data, int initial_len);
struct ssh_rportfwd;
/* Structure definition centralised here because the SSH-1 and SSH-2
* connection layers both use it. But the client module (portfwd.c)
* should not try to look inside here. */
struct ssh_rportfwd {
unsigned sport, dport;
char *shost, *dhost;
int addressfamily;
char *log_description; /* name of remote listening port, for logging */
ssh_sharing_connstate *share_ctx;
PortFwdRecord *pfr;
};
void free_rportfwd(struct ssh_rportfwd *rpf);
struct ConnectionLayerVtable {
/* Allocate and free remote-to-local port forwardings, called by
@ -227,6 +238,25 @@ struct ConnectionLayerVtable {
/* Query whether the connection layer is doing agent forwarding */
int (*agent_forwarding_permitted)(ConnectionLayer *cl);
/* Set the size of the main terminal window (if any) */
void (*terminal_size)(ConnectionLayer *cl, int width, int height);
/* Indicate that the backlog on standard output has cleared */
void (*stdout_unthrottle)(ConnectionLayer *cl, int bufsize);
/* Query the size of the backlog on standard _input_ */
int (*stdin_backlog)(ConnectionLayer *cl);
/* Tell the connection layer that the SSH connection itself has
* backed up, so it should tell all currently open channels to
* cease reading from their local input sources if they can. (Or
* tell it that that state of affairs has gone away again.) */
void (*throttle_all_channels)(ConnectionLayer *cl, int throttled);
/* Ask the connection layer about its current preference for
* line-discipline options. */
int (*ldisc_option)(ConnectionLayer *cl, int option);
};
struct ConnectionLayer {
@ -253,6 +283,13 @@ struct ConnectionLayer {
((cl)->vt->sharing_queue_global_request(cl, cs))
#define ssh_agent_forwarding_permitted(cl) \
((cl)->vt->agent_forwarding_permitted(cl))
#define ssh_terminal_size(cl, w, h) ((cl)->vt->terminal_size(cl, w, h))
#define ssh_stdout_unthrottle(cl, bufsize) \
((cl)->vt->stdout_unthrottle(cl, bufsize))
#define ssh_stdin_backlog(cl) ((cl)->vt->stdin_backlog(cl))
#define ssh_throttle_all_channels(cl, throttled) \
((cl)->vt->throttle_all_channels(cl, throttled))
#define ssh_ldisc_option(cl, option) ((cl)->vt->ldisc_option(cl, option))
/* Exports from portfwd.c */
PortFwdManager *portfwdmgr_new(ConnectionLayer *cl);
@ -266,6 +303,19 @@ char *portfwdmgr_connect(PortFwdManager *mgr, Channel **chan_ret,
Frontend *ssh_get_frontend(Ssh ssh);
/* Communications back to ssh.c from connection layers */
void ssh_throttle_conn(Ssh ssh, int adjust);
void ssh_got_exitcode(Ssh ssh, int status);
void ssh_ldisc_update(Ssh ssh);
void ssh_got_fallback_cmd(Ssh ssh);
/* Functions to abort the connection, for various reasons. */
void ssh_remote_error(Ssh ssh, const char *fmt, ...);
void ssh_remote_eof(Ssh ssh, const char *fmt, ...);
void ssh_proto_error(Ssh ssh, const char *fmt, ...);
void ssh_sw_abort(Ssh ssh, const char *fmt, ...);
void ssh_user_close(Ssh ssh, const char *fmt, ...);
#define SSH_CIPHER_IDEA 1
#define SSH_CIPHER_DES 2
#define SSH_CIPHER_3DES 3
@ -1241,10 +1291,6 @@ enum {
#undef DEF_ENUM_UNIVERSAL
#undef DEF_ENUM_CONTEXTUAL
/* Given that virtual packet types exist, this is how big the dispatch
* table has to be */
#define SSH_MAX_MSG 0x101
/*
* SSH-1 agent messages.
*/

View File

@ -88,8 +88,11 @@ void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
#define BPP_READ(ptr, len) do \
{ \
crMaybeWaitUntilV(bufchain_try_fetch_consume( \
crMaybeWaitUntilV(s->bpp.input_eof || \
bufchain_try_fetch_consume( \
s->bpp.in_raw, ptr, len)); \
if (s->bpp.input_eof) \
goto eof; \
} while (0)
static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
@ -109,9 +112,9 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
}
if (s->len < 0 || s->len > 262144) { /* SSH1.5-mandated max size */
s->bpp.error = dupprintf(
"Extremely large packet length from server suggests"
" data stream corruption");
ssh_sw_abort(s->bpp.ssh,
"Extremely large packet length from server suggests"
" data stream corruption");
crStopV;
}
@ -134,8 +137,8 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
if (s->cipher && detect_attack(s->crcda_ctx,
s->data, s->biglen, NULL)) {
s->bpp.error = dupprintf(
"Network attack (CRC compensation) detected!");
ssh_sw_abort(s->bpp.ssh,
"Network attack (CRC compensation) detected!");
crStopV;
}
@ -145,8 +148,7 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
s->realcrc = crc32_compute(s->data, s->biglen - 4);
s->gotcrc = GET_32BIT(s->data + s->biglen - 4);
if (s->gotcrc != s->realcrc) {
s->bpp.error = dupprintf(
"Incorrect CRC received on packet");
ssh_sw_abort(s->bpp.ssh, "Incorrect CRC received on packet");
crStopV;
}
@ -156,8 +158,8 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
if (!ssh_decompressor_decompress(
s->decompctx, s->data + s->pad, s->length + 1,
&decompblk, &decomplen)) {
s->bpp.error = dupprintf(
"Zlib decompression encountered invalid data");
ssh_sw_abort(s->bpp.ssh,
"Zlib decompression encountered invalid data");
crStopV;
}
@ -204,10 +206,6 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
s->pktin = NULL;
switch (type) {
case SSH1_MSG_DISCONNECT:
s->bpp.seen_disconnect = TRUE;
break;
case SSH1_SMSG_SUCCESS:
case SSH1_SMSG_FAILURE:
if (s->pending_compression_request) {
@ -239,6 +237,15 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
}
}
}
eof:
if (!s->bpp.expect_close) {
ssh_remote_error(s->bpp.ssh,
"Server unexpectedly closed network connection");
} else {
ssh_remote_eof(s->bpp.ssh, "Server closed network connection");
}
crFinishV;
}

1211
ssh1connection.c Normal file

File diff suppressed because it is too large Load Diff

1206
ssh1login.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -52,8 +52,11 @@ static void ssh2_bare_bpp_free(BinaryPacketProtocol *bpp)
#define BPP_READ(ptr, len) do \
{ \
crMaybeWaitUntilV(bufchain_try_fetch_consume( \
crMaybeWaitUntilV(s->bpp.input_eof || \
bufchain_try_fetch_consume( \
s->bpp.in_raw, ptr, len)); \
if (s->bpp.input_eof) \
goto eof; \
} while (0)
static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp)
@ -72,7 +75,7 @@ static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp)
}
if (s->packetlen <= 0 || s->packetlen >= (long)OUR_V2_PACKETLIMIT) {
s->bpp.error = dupstr("Invalid packet length received");
ssh_sw_abort(s->bpp.ssh, "Invalid packet length received");
crStopV;
}
@ -123,15 +126,17 @@ static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp)
}
pq_push(&s->bpp.in_pq, s->pktin);
{
int type = s->pktin->type;
s->pktin = NULL;
if (type == SSH2_MSG_DISCONNECT)
s->bpp.seen_disconnect = TRUE;
}
s->pktin = NULL;
}
eof:
if (!s->bpp.expect_close) {
ssh_remote_error(s->bpp.ssh,
"Server unexpectedly closed network connection");
} else {
ssh_remote_eof(s->bpp.ssh, "Server closed network connection");
}
crFinishV;
}

View File

@ -168,8 +168,11 @@ void ssh2_bpp_new_incoming_crypto(
#define BPP_READ(ptr, len) do \
{ \
crMaybeWaitUntilV(bufchain_try_fetch_consume( \
crMaybeWaitUntilV(s->bpp.input_eof || \
bufchain_try_fetch_consume( \
s->bpp.in_raw, ptr, len)); \
if (s->bpp.input_eof) \
goto eof; \
} while (0)
static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
@ -246,8 +249,8 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
s->packetlen-4))
break;
if (s->packetlen >= (long)OUR_V2_PACKETLIMIT) {
s->bpp.error = dupprintf(
"No valid incoming packet found");
ssh_sw_abort(s->bpp.ssh,
"No valid incoming packet found");
crStopV;
}
}
@ -293,8 +296,8 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
*/
if (s->len < 0 || s->len > (long)OUR_V2_PACKETLIMIT ||
s->len % s->cipherblk != 0) {
s->bpp.error = dupprintf(
"Incoming packet length field was garbled");
ssh_sw_abort(s->bpp.ssh,
"Incoming packet length field was garbled");
crStopV;
}
@ -323,7 +326,7 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
*/
if (s->in.mac && !ssh2_mac_verify(
s->in.mac, s->data, s->len + 4, s->in.sequence)) {
s->bpp.error = dupprintf("Incorrect MAC received on packet");
ssh_sw_abort(s->bpp.ssh, "Incorrect MAC received on packet");
crStopV;
}
@ -358,8 +361,8 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
*/
if (s->len < 0 || s->len > (long)OUR_V2_PACKETLIMIT ||
(s->len + 4) % s->cipherblk != 0) {
s->bpp.error = dupprintf(
"Incoming packet was garbled on decryption");
ssh_sw_abort(s->bpp.ssh,
"Incoming packet was garbled on decryption");
crStopV;
}
@ -396,15 +399,15 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
*/
if (s->in.mac && !ssh2_mac_verify(
s->in.mac, s->data, s->len + 4, s->in.sequence)) {
s->bpp.error = dupprintf("Incorrect MAC received on packet");
ssh_sw_abort(s->bpp.ssh, "Incorrect MAC received on packet");
crStopV;
}
}
/* Get and sanity-check the amount of random padding. */
s->pad = s->data[4];
if (s->pad < 4 || s->len - s->pad < 1) {
s->bpp.error = dupprintf(
"Invalid padding length on received packet");
ssh_sw_abort(s->bpp.ssh,
"Invalid padding length on received packet");
crStopV;
}
/*
@ -492,8 +495,6 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
int type = s->pktin->type;
s->pktin = NULL;
if (type == SSH2_MSG_DISCONNECT)
s->bpp.seen_disconnect = TRUE;
if (type == SSH2_MSG_NEWKEYS) {
/*
* Mild layer violation: in this situation we must
@ -506,6 +507,15 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
}
}
}
eof:
if (!s->bpp.expect_close) {
ssh_remote_error(s->bpp.ssh,
"Server unexpectedly closed network connection");
} else {
ssh_remote_eof(s->bpp.ssh, "Server closed network connection");
}
crFinishV;
}

2501
ssh2connection.c Normal file

File diff suppressed because it is too large Load Diff

2967
ssh2transport.c Normal file

File diff suppressed because it is too large Load Diff

1673
ssh2userauth.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,7 @@ struct BinaryPacketProtocolVtable {
struct BinaryPacketProtocol {
const struct BinaryPacketProtocolVtable *vt;
bufchain *in_raw, *out_raw;
int input_eof; /* set this if in_raw will never be added to again */
PktInQueue in_pq;
PktOutQueue out_pq;
PacketLogSettings *pls;
@ -34,8 +35,11 @@ struct BinaryPacketProtocol {
int remote_bugs;
int seen_disconnect;
char *error;
/* Set this if remote connection closure should not generate an
* error message (either because it's not to be treated as an
* error at all, or because some other error message has already
* been emitted). */
int expect_close;
};
#define ssh_bpp_handle_input(bpp) ((bpp)->vt->handle_input(bpp))

View File

@ -9,6 +9,7 @@
#include "putty.h"
#include "ssh.h"
#include "sshbpp.h"
#include "sshppl.h"
#include "sshchan.h"
/* ----------------------------------------------------------------------
@ -630,6 +631,72 @@ const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type)
#undef TRANSLATE_KEX
#undef TRANSLATE_AUTH
/* ----------------------------------------------------------------------
* Common helper function for clients and implementations of
* PacketProtocolLayer.
*/
void ssh_logevent_and_free(void *frontend, char *message)
{
logevent(frontend, message);
sfree(message);
}
void ssh_ppl_replace(PacketProtocolLayer *old, PacketProtocolLayer *new)
{
new->bpp = old->bpp;
ssh_ppl_setup_queues(new, old->in_pq, old->out_pq);
new->selfptr = old->selfptr;
new->user_input = old->user_input;
new->frontend = old->frontend;
new->ssh = old->ssh;
*new->selfptr = new;
ssh_ppl_free(old);
/* The new layer might need to be the first one that sends a
* packet, so trigger a call to its main coroutine immediately. If
* it doesn't need to go first, the worst that will do is return
* straight away. */
queue_idempotent_callback(&new->ic_process_queue);
}
void ssh_ppl_free(PacketProtocolLayer *ppl)
{
delete_callbacks_for_context(ppl);
ppl->vt->free(ppl);
}
static void ssh_ppl_ic_process_queue_callback(void *context)
{
PacketProtocolLayer *ppl = (PacketProtocolLayer *)context;
ssh_ppl_process_queue(ppl);
}
void ssh_ppl_setup_queues(PacketProtocolLayer *ppl,
PktInQueue *inq, PktOutQueue *outq)
{
ppl->in_pq = inq;
ppl->out_pq = outq;
ppl->in_pq->pqb.ic = &ppl->ic_process_queue;
ppl->ic_process_queue.fn = ssh_ppl_ic_process_queue_callback;
ppl->ic_process_queue.ctx = ppl;
/* If there's already something on the input queue, it will want
* handling immediately. */
if (pq_peek(ppl->in_pq))
queue_idempotent_callback(&ppl->ic_process_queue);
}
void ssh_ppl_user_output_string_and_free(PacketProtocolLayer *ppl, char *text)
{
/* Messages sent via this function are from the SSH layer, not
* from the server-side process, so they always have the stderr
* flag set. */
from_backend(ppl->frontend, TRUE, text, strlen(text));
sfree(text);
}
/* ----------------------------------------------------------------------
* Common helper functions for clients and implementations of
* BinaryPacketProtocol.
@ -651,6 +718,7 @@ void ssh_bpp_common_setup(BinaryPacketProtocol *bpp)
{
pq_in_init(&bpp->in_pq);
pq_out_init(&bpp->out_pq);
bpp->input_eof = FALSE;
bpp->ic_in_raw.fn = ssh_bpp_input_raw_data_callback;
bpp->ic_in_raw.ctx = bpp;
bpp->ic_out_pq.fn = ssh_bpp_output_packet_callback;
@ -764,3 +832,37 @@ int verify_ssh_manual_host_key(
return 0;
}
/* ----------------------------------------------------------------------
* Common get_specials function for the two SSH-1 layers.
*/
int ssh1_common_get_specials(
PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx)
{
/*
* 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 (!(ppl->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) {
add_special(ctx, "IGNORE message", SS_NOP, 0);
return TRUE;
}
return FALSE;
}
/* ----------------------------------------------------------------------
* Other miscellaneous utility functions.
*/
void free_rportfwd(struct ssh_rportfwd *rpf)
{
if (rpf) {
sfree(rpf->log_description);
sfree(rpf->shost);
sfree(rpf->dhost);
sfree(rpf);
}
}

View File

@ -200,6 +200,18 @@ struct ssh_gss_library {
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*/

144
sshppl.h Normal file
View File

@ -0,0 +1,144 @@
/*
* Abstraction of the various layers of SSH packet-level protocol,
* general enough to take in all three of the main SSH-2 layers and
* both of the SSH-1 phases.
*/
#ifndef PUTTY_SSHPPL_H
#define PUTTY_SSHPPL_H
typedef void (*packet_handler_fn_t)(PacketProtocolLayer *ppl, PktIn *pktin);
typedef void (*add_special_fn_t)(
void *ctx, const char *text, SessionSpecialCode code, int arg);
struct PacketProtocolLayerVtable {
void (*free)(PacketProtocolLayer *);
void (*process_queue)(PacketProtocolLayer *ppl);
int (*get_specials)(
PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx);
void (*special_cmd)(
PacketProtocolLayer *ppl, SessionSpecialCode code, int arg);
int (*want_user_input)(PacketProtocolLayer *ppl);
void (*got_user_input)(PacketProtocolLayer *ppl);
void (*reconfigure)(PacketProtocolLayer *ppl, Conf *conf);
/* Protocol-level name of this layer. */
const char *name;
};
struct PacketProtocolLayer {
const struct PacketProtocolLayerVtable *vt;
/* Link to the underlying SSH BPP. */
BinaryPacketProtocol *bpp;
/* Queue from which the layer receives its input packets, and one
* to put its output packets on. */
PktInQueue *in_pq;
PktOutQueue *out_pq;
/* Idempotent callback that in_pq will be linked to, causing a
* call to the process_queue method. in_pq points to this, so it
* will be automatically triggered by pushing things on the
* layer's input queue, but it can also be triggered on purpose. */
IdempotentCallback ic_process_queue;
/* Owner's pointer to this layer. Permits a layer to unilaterally
* abdicate in favour of a replacement, by overwriting this
* pointer and then freeing itself. */
PacketProtocolLayer **selfptr;
/* Bufchain of keyboard input from the user, for login prompts and
* similar. */
bufchain *user_input;
/* Logging and error-reporting facilities. */
void *frontend; /* for logevent, dialog boxes etc */
Ssh ssh; /* for session termination + assorted connection-layer ops */
/* Known bugs in the remote implementation. */
unsigned remote_bugs;
};
#define ssh_ppl_process_queue(ppl) ((ppl)->vt->process_queue(ppl))
#define ssh_ppl_get_specials(ppl, add, ctx) \
((ppl)->vt->get_specials(ppl, add, ctx))
#define ssh_ppl_special_cmd(ppl, code, arg) \
((ppl)->vt->special_cmd(ppl, code, arg))
#define ssh_ppl_want_user_input(ppl) ((ppl)->vt->want_user_input(ppl))
#define ssh_ppl_got_user_input(ppl) ((ppl)->vt->got_user_input(ppl))
#define ssh_ppl_reconfigure(ppl, conf) ((ppl)->vt->reconfigure(ppl, conf))
/* ssh_ppl_free is more than just a macro wrapper on the vtable; it
* does centralised parts of the freeing too. */
void ssh_ppl_free(PacketProtocolLayer *ppl);
/* Helper routine to point a PPL at its input and output queues. Also
* sets up the IdempotentCallback on the input queue to trigger a call
* to process_queue whenever packets are added to it. */
void ssh_ppl_setup_queues(PacketProtocolLayer *ppl,
PktInQueue *inq, PktOutQueue *outq);
/* Routine a PPL can call to abdicate in favour of a replacement, by
* overwriting ppl->selfptr. Has the side effect of freeing 'old', so
* if 'old' actually called this (which is likely) then it should
* avoid dereferencing itself on return from this function! */
void ssh_ppl_replace(PacketProtocolLayer *old, PacketProtocolLayer *new);
PacketProtocolLayer *ssh1_login_new(
Conf *conf, const char *host, int port,
PacketProtocolLayer *successor_layer);
PacketProtocolLayer *ssh1_connection_new(
Ssh ssh, Conf *conf, ConnectionLayer **cl_out);
struct DataTransferStats;
struct ssh_connection_shared_gss_state;
PacketProtocolLayer *ssh2_transport_new(
Conf *conf, const char *host, int port, const char *fullhostname,
const char *client_greeting, const char *server_greeting,
struct ssh_connection_shared_gss_state *shgss,
struct DataTransferStats *stats,
PacketProtocolLayer *higher_layer);
PacketProtocolLayer *ssh2_userauth_new(
PacketProtocolLayer *successor_layer,
const char *hostname, const char *fullhostname,
Filename *keyfile, int tryagent,
const char *default_username, int change_username,
int try_ki_auth,
int try_gssapi_auth, int try_gssapi_kex_auth,
int gssapi_fwd, struct ssh_connection_shared_gss_state *shgss);
PacketProtocolLayer *ssh2_connection_new(
Ssh ssh, ssh_sharing_state *connshare, int is_simple,
Conf *conf, const char *peer_verstring, ConnectionLayer **cl_out);
/* Can't put this in the userauth constructor without having a
* dependency loop at setup time (transport and userauth can't _both_
* be constructed second and given a pointer to the other). */
void ssh2_userauth_set_transport_layer(PacketProtocolLayer *userauth,
PacketProtocolLayer *transport);
/* Convenience macro for protocol layers to send formatted strings to
* the Event Log. Assumes a function parameter called 'ppl' is in
* scope, and takes a double pair of parens because it passes a whole
* argument list to dupprintf. */
#define ppl_logevent(params) ( \
logevent_and_free((ppl)->frontend, dupprintf params))
/* Convenience macro for protocol layers to send formatted strings to
* the terminal. Also expects 'ppl' to be in scope and takes double
* parens. */
#define ppl_printf(params) \
ssh_ppl_user_output_string_and_free(ppl, dupprintf params)
void ssh_ppl_user_output_string_and_free(PacketProtocolLayer *ppl, char *text);
/* Methods for userauth to communicate back to the transport layer */
ptrlen ssh2_transport_get_session_id(PacketProtocolLayer *ssh2_transport_ptr);
void ssh2_transport_notify_auth_done(PacketProtocolLayer *ssh2_transport_ptr);
/* Methods for ssh1login to pass protocol flags to ssh1connection */
void ssh1_connection_set_local_protoflags(PacketProtocolLayer *ppl, int flags);
/* Shared get_specials method between the two ssh1 layers */
int ssh1_common_get_specials(PacketProtocolLayer *, add_special_fn_t, void *);
#endif /* PUTTY_SSHPPL_H */

View File

@ -204,7 +204,10 @@ static void ssh_verstring_send(struct ssh_verstring_state *s)
#define BPP_WAITFOR(minlen) do \
{ \
crMaybeWaitUntilV( \
s->bpp.input_eof || \
bufchain_size(s->bpp.in_raw) >= (minlen)); \
if (s->bpp.input_eof) \
goto eof; \
} while (0)
void ssh_verstring_handle_input(BinaryPacketProtocol *bpp)
@ -359,13 +362,14 @@ void ssh_verstring_handle_input(BinaryPacketProtocol *bpp)
* Unable to agree on a major protocol version at all.
*/
if (!ssh_version_includes_v2(s->our_protoversion)) {
s->bpp.error = dupstr(
"SSH protocol version 1 required by our configuration "
"but not provided by remote");
ssh_sw_abort(s->bpp.ssh,
"SSH protocol version 1 required by our "
"configuration but not provided by remote");
} else {
s->bpp.error = dupstr(
"SSH protocol version 2 required by our configuration "
"but remote only provides (old, insecure) SSH-1");
ssh_sw_abort(s->bpp.ssh,
"SSH protocol version 2 required by our "
"configuration but remote only provides "
"(old, insecure) SSH-1");
}
crStopV;
}
@ -387,6 +391,11 @@ void ssh_verstring_handle_input(BinaryPacketProtocol *bpp)
* done.
*/
s->receiver->got_ssh_version(s->receiver, s->major_protoversion);
return;
eof:
ssh_remote_error(s->bpp.ssh,
"Server unexpectedly closed network connection");
crFinishV;
}