1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-08 08:58:00 +00:00
putty-source/ssh1connection.c
Simon Tatham b4e1110892 Relax criteria for accepting agent-forwarding channel-opens.
Previously, the instant at which we send to the server a request to
enable agent forwarding (the "auth-agent-req@openssh.com" channel
request, or SSH1_CMSG_AGENT_REQUEST_FORWARDING) was also the instant
at which we set a flag indicating that we're prepared to accept
attempts from the server to open a channel to talk to the forwarded
agent. If the server attempts that when we haven't sent a forwarding
request, we treat it with suspicion, and reject it.

But it turns out that at least one SSH server does this, for what
seems to be a _somewhat_ sensible purpose, and OpenSSH accepts it. So,
on the basis that the @openssh.com domain suffix makes them the
arbiters of this part of the spec, I'm following their practice. I've
removed the 'agent_fwd_enabled' flag from both connection layer
implementations, together with the ConnectionLayer method that sets
it; now agent-forwarding CHANNEL_OPENs are gated only on the questions
of whether agent forwarding was permitted in the configuration and
whether an agent actually exists to talk to, and not also whether we
had previously sent a message to the server announcing it.

(The change to this condition is also applied in the SSH-1 agent
forwarding code, mostly for the sake of keeping things parallel where
possible. I think it doesn't actually make a difference in SSH-1,
because in SSH-1, it's not _possible_ for the server to try to open an
agent channel before the main channel is set up, due to the entirely
separate setup phase of the protocol.)

The use case is a proxy host which makes a secondary SSH connection to
a real destination host. A user has run into one of these recently,
announcing a version banner of "SSH-2.0-FudoSSH", which relies on
agent forwarding to authenticate the secondary connection. You connect
to the proxy host and authenticate with a username string of the form
"realusername#real.destination.host", and then, at the start of the
connection protocol, the server immediately opens a channel back to
your SSH agent which it uses to authenticate to the destination host.
And it delays answering any CHANNEL_OPEN requests from the client
until that's all done. For example (seen from the client's POV,
although the server's CHANNEL_OPEN may well have been _sent_ up front
rather than in response to the client's):

client: SSH2_MSG_CHANNEL_OPEN "session"
server: SSH2_MSG_CHANNEL_OPEN "auth-agent@openssh.com"
client: SSH2_MSG_CHANNEL_OPEN_CONFIRMATION to the auth-agent request
        <- data is exchanged on the agent channel; proxy host uses
           that signature to log in to the destination host ->
server: SSH2_MSG_CHANNEL_OPEN_CONFIRMATION to the session request

With PuTTY, this wasn't working, because at the point when the server
sends the auth-agent CHANNEL_OPEN, we had not yet had any opportunity
to send auth-agent-req (because that has to wait until we've had a
CHANNEL_OPEN_CONFIRMATION). So we were rejecting the server's
CHANNEL_OPEN, which broke this workflow:

client: SSH2_MSG_CHANNEL_OPEN "session"
server: SSH2_MSG_CHANNEL_OPEN "auth-agent@openssh.com"
client: SSH2_MSG_CHANNEL_OPEN_FAILURE to the auth-agent request
        (hey, I haven't told you you can do that yet!)
server: SSH2_MSG_CHANNEL_OPEN_FAILURE to the session request
        (in that case, no shell session for you!)
2020-12-23 22:26:44 +00:00

816 lines
26 KiB
C

/*
* Packet protocol layer for the SSH-1 'connection protocol', i.e.
* everything after authentication finishes.
*/
#include <assert.h>
#include "putty.h"
#include "ssh.h"
#include "sshbpp.h"
#include "sshppl.h"
#include "sshchan.h"
#include "sshcr.h"
#include "ssh1connection.h"
static int ssh1_rportfwd_cmp(void *av, void *bv)
{
struct ssh_rportfwd *a = (struct ssh_rportfwd *) av;
struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv;
int i;
if ( (i = strcmp(a->dhost, b->dhost)) != 0)
return i < 0 ? -1 : +1;
if (a->dport > b->dport)
return +1;
if (a->dport < b->dport)
return -1;
return 0;
}
static void ssh1_connection_free(PacketProtocolLayer *);
static void ssh1_connection_process_queue(PacketProtocolLayer *);
static void ssh1_connection_special_cmd(PacketProtocolLayer *ppl,
SessionSpecialCode code, int arg);
static bool ssh1_connection_want_user_input(PacketProtocolLayer *ppl);
static void ssh1_connection_got_user_input(PacketProtocolLayer *ppl);
static void ssh1_connection_reconfigure(PacketProtocolLayer *ppl, Conf *conf);
static const PacketProtocolLayerVtable ssh1_connection_vtable = {
.free = ssh1_connection_free,
.process_queue = ssh1_connection_process_queue,
.get_specials = ssh1_common_get_specials,
.special_cmd = ssh1_connection_special_cmd,
.want_user_input = ssh1_connection_want_user_input,
.got_user_input = ssh1_connection_got_user_input,
.reconfigure = ssh1_connection_reconfigure,
.queued_data_size = ssh_ppl_default_queued_data_size,
.name = NULL, /* no layer names in SSH-1 */
};
static void ssh1_rportfwd_remove(
ConnectionLayer *cl, struct ssh_rportfwd *rpf);
static SshChannel *ssh1_lportfwd_open(
ConnectionLayer *cl, const char *hostname, int port,
const char *description, const SocketPeerInfo *pi, Channel *chan);
static struct X11FakeAuth *ssh1_add_x11_display(
ConnectionLayer *cl, int authtype, struct X11Display *disp);
static bool ssh1_agent_forwarding_permitted(ConnectionLayer *cl);
static void ssh1_terminal_size(ConnectionLayer *cl, int width, int height);
static void ssh1_stdout_unthrottle(ConnectionLayer *cl, size_t bufsize);
static size_t ssh1_stdin_backlog(ConnectionLayer *cl);
static void ssh1_throttle_all_channels(ConnectionLayer *cl, bool throttled);
static bool ssh1_ldisc_option(ConnectionLayer *cl, int option);
static void ssh1_set_ldisc_option(ConnectionLayer *cl, int option, bool value);
static void ssh1_enable_x_fwd(ConnectionLayer *cl);
static void ssh1_set_wants_user_input(ConnectionLayer *cl, bool wanted);
static const ConnectionLayerVtable ssh1_connlayer_vtable = {
.rportfwd_alloc = ssh1_rportfwd_alloc,
.rportfwd_remove = ssh1_rportfwd_remove,
.lportfwd_open = ssh1_lportfwd_open,
.session_open = ssh1_session_open,
.serverside_x11_open = ssh1_serverside_x11_open,
.serverside_agent_open = ssh1_serverside_agent_open,
.add_x11_display = ssh1_add_x11_display,
.agent_forwarding_permitted = ssh1_agent_forwarding_permitted,
.terminal_size = ssh1_terminal_size,
.stdout_unthrottle = ssh1_stdout_unthrottle,
.stdin_backlog = ssh1_stdin_backlog,
.throttle_all_channels = ssh1_throttle_all_channels,
.ldisc_option = ssh1_ldisc_option,
.set_ldisc_option = ssh1_set_ldisc_option,
.enable_x_fwd = ssh1_enable_x_fwd,
.set_wants_user_input = ssh1_set_wants_user_input,
/* other methods are NULL */
};
static size_t ssh1channel_write(
SshChannel *c, bool is_stderr, const void *buf, size_t len);
static void ssh1channel_write_eof(SshChannel *c);
static void ssh1channel_initiate_close(SshChannel *c, const char *err);
static void ssh1channel_unthrottle(SshChannel *c, size_t bufsize);
static Conf *ssh1channel_get_conf(SshChannel *c);
static void ssh1channel_window_override_removed(SshChannel *c) { /* ignore */ }
static const SshChannelVtable ssh1channel_vtable = {
.write = ssh1channel_write,
.write_eof = ssh1channel_write_eof,
.initiate_close = ssh1channel_initiate_close,
.unthrottle = ssh1channel_unthrottle,
.get_conf = ssh1channel_get_conf,
.window_override_removed = ssh1channel_window_override_removed,
/* everything else is NULL */
};
static void ssh1_channel_try_eof(struct ssh1_channel *c);
static void ssh1_channel_close_local(struct ssh1_channel *c,
const char *reason);
static void ssh1_channel_destroy(struct ssh1_channel *c);
static void ssh1_channel_check_close(struct ssh1_channel *c);
static int ssh1_channelcmp(void *av, void *bv)
{
const struct ssh1_channel *a = (const struct ssh1_channel *) av;
const struct ssh1_channel *b = (const struct ssh1_channel *) bv;
if (a->localid < b->localid)
return -1;
if (a->localid > b->localid)
return +1;
return 0;
}
static int ssh1_channelfind(void *av, void *bv)
{
const unsigned *a = (const unsigned *) av;
const struct ssh1_channel *b = (const struct ssh1_channel *) bv;
if (*a < b->localid)
return -1;
if (*a > b->localid)
return +1;
return 0;
}
void ssh1_channel_free(struct ssh1_channel *c)
{
if (c->chan)
chan_free(c->chan);
sfree(c);
}
PacketProtocolLayer *ssh1_connection_new(
Ssh *ssh, Conf *conf, ConnectionLayer **cl_out)
{
struct ssh1_connection_state *s = snew(struct ssh1_connection_state);
memset(s, 0, sizeof(*s));
s->ppl.vt = &ssh1_connection_vtable;
s->conf = conf_copy(conf);
s->channels = newtree234(ssh1_channelcmp);
s->x11authtree = newtree234(x11_authcmp);
/* Need to get the log context for s->cl now, because we won't be
* helpfully notified when a copy is written into s->ppl by our
* owner. */
s->cl.vt = &ssh1_connlayer_vtable;
s->cl.logctx = ssh_get_logctx(ssh);
s->portfwdmgr = portfwdmgr_new(&s->cl);
s->rportfwds = newtree234(ssh1_rportfwd_cmp);
*cl_out = &s->cl;
return &s->ppl;
}
static void ssh1_connection_free(PacketProtocolLayer *ppl)
{
struct ssh1_connection_state *s =
container_of(ppl, struct ssh1_connection_state, ppl);
struct X11FakeAuth *auth;
struct ssh1_channel *c;
struct ssh_rportfwd *rpf;
conf_free(s->conf);
while ((c = delpos234(s->channels, 0)) != NULL)
ssh1_channel_free(c);
freetree234(s->channels);
if (s->mainchan_chan)
chan_free(s->mainchan_chan);
if (s->x11disp)
x11_free_display(s->x11disp);
while ((auth = delpos234(s->x11authtree, 0)) != NULL)
x11_free_fake_auth(auth);
freetree234(s->x11authtree);
while ((rpf = delpos234(s->rportfwds, 0)) != NULL)
free_rportfwd(rpf);
freetree234(s->rportfwds);
portfwdmgr_free(s->portfwdmgr);
if (s->antispoof_prompt)
free_prompts(s->antispoof_prompt);
delete_callbacks_for_context(s);
sfree(s);
}
void ssh1_connection_set_protoflags(PacketProtocolLayer *ppl,
int local, int remote)
{
assert(ppl->vt == &ssh1_connection_vtable);
struct ssh1_connection_state *s =
container_of(ppl, struct ssh1_connection_state, ppl);
s->local_protoflags = local;
s->remote_protoflags = remote;
}
static bool ssh1_connection_filter_queue(struct ssh1_connection_state *s)
{
PktIn *pktin;
ptrlen data;
struct ssh1_channel *c;
unsigned localid;
bool expect_halfopen;
while (1) {
if (ssh1_common_filter_queue(&s->ppl))
return true;
if ((pktin = pq_peek(s->ppl.in_pq)) == NULL)
return false;
switch (pktin->type) {
case SSH1_MSG_CHANNEL_DATA:
case SSH1_MSG_CHANNEL_OPEN_CONFIRMATION:
case SSH1_MSG_CHANNEL_OPEN_FAILURE:
case SSH1_MSG_CHANNEL_CLOSE:
case SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION:
/*
* Common preliminary code for all the messages from the
* server that cite one of our channel ids: look up that
* channel id, check it exists, and if it's for a sharing
* downstream, pass it on.
*/
localid = get_uint32(pktin);
c = find234(s->channels, &localid, ssh1_channelfind);
expect_halfopen = (
pktin->type == SSH1_MSG_CHANNEL_OPEN_CONFIRMATION ||
pktin->type == SSH1_MSG_CHANNEL_OPEN_FAILURE);
if (!c || c->halfopen != expect_halfopen) {
ssh_remote_error(
s->ppl.ssh, "Received %s for %s channel %u",
ssh1_pkt_type(pktin->type),
!c ? "nonexistent" : c->halfopen ? "half-open" : "open",
localid);
return true;
}
switch (pktin->type) {
case SSH1_MSG_CHANNEL_OPEN_CONFIRMATION:
assert(c->halfopen);
c->remoteid = get_uint32(pktin);
c->halfopen = false;
c->throttling_conn = false;
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 ssh1_channel_check_close
* will consult) will already be returning true.
*/
ssh1_channel_check_close(c);
if (c->pending_eof)
ssh1_channel_try_eof(c); /* in case we had a pending EOF */
break;
case SSH1_MSG_CHANNEL_OPEN_FAILURE:
assert(c->halfopen);
chan_open_failed(c->chan, NULL);
chan_free(c->chan);
del234(s->channels, c);
ssh1_channel_free(c);
break;
case SSH1_MSG_CHANNEL_DATA:
data = get_string(pktin);
if (!get_err(pktin)) {
int bufsize = chan_send(
c->chan, false, data.ptr, data.len);
if (!c->throttling_conn && bufsize > SSH1_BUFFER_LIMIT) {
c->throttling_conn = true;
ssh_throttle_conn(s->ppl.ssh, +1);
}
}
break;
case SSH1_MSG_CHANNEL_CLOSE:
if (!(c->closes & CLOSES_RCVD_CLOSE)) {
c->closes |= CLOSES_RCVD_CLOSE;
chan_send_eof(c->chan);
ssh1_channel_check_close(c);
}
break;
case SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION:
if (!(c->closes & CLOSES_RCVD_CLOSECONF)) {
if (!(c->closes & CLOSES_SENT_CLOSE)) {
ssh_remote_error(
s->ppl.ssh,
"Received CHANNEL_CLOSE_CONFIRMATION for channel"
" %u for which we never sent CHANNEL_CLOSE\n",
c->localid);
return true;
}
c->closes |= CLOSES_RCVD_CLOSECONF;
ssh1_channel_check_close(c);
}
break;
}
pq_pop(s->ppl.in_pq);
break;
default:
if (ssh1_handle_direction_specific_packet(s, pktin)) {
pq_pop(s->ppl.in_pq);
if (ssh1_check_termination(s))
return true;
} else {
return false;
}
}
}
}
static PktIn *ssh1_connection_pop(struct ssh1_connection_state *s)
{
ssh1_connection_filter_queue(s);
return pq_pop(s->ppl.in_pq);
}
static void ssh1_connection_process_queue(PacketProtocolLayer *ppl)
{
struct ssh1_connection_state *s =
container_of(ppl, struct ssh1_connection_state, ppl);
PktIn *pktin;
if (ssh1_connection_filter_queue(s)) /* no matter why we were called */
return;
crBegin(s->crState);
/*
* Signal the seat that authentication is done, so that it can
* deploy spoofing defences. If it doesn't have any, deploy our
* own fallback one.
*
* We do this here rather than at the end of userauth, because we
* might not have gone through userauth at all (if we're a
* connection-sharing downstream).
*/
if (ssh1_connection_need_antispoof_prompt(s)) {
s->antispoof_prompt = new_prompts();
s->antispoof_prompt->to_server = true;
s->antispoof_prompt->from_server = false;
s->antispoof_prompt->name = dupstr("Authentication successful");
add_prompt(
s->antispoof_prompt,
dupstr("Access granted. Press Return to begin session. "), false);
s->antispoof_ret = seat_get_userpass_input(
s->ppl.seat, s->antispoof_prompt, NULL);
while (1) {
while (s->antispoof_ret < 0 &&
bufchain_size(s->ppl.user_input) > 0)
s->antispoof_ret = seat_get_userpass_input(
s->ppl.seat, s->antispoof_prompt, s->ppl.user_input);
if (s->antispoof_ret >= 0)
break;
s->want_user_input = true;
crReturnV;
s->want_user_input = false;
}
free_prompts(s->antispoof_prompt);
s->antispoof_prompt = NULL;
}
portfwdmgr_config(s->portfwdmgr, s->conf);
s->portfwdmgr_configured = true;
while (!s->finished_setup) {
ssh1_connection_direction_specific_setup(s);
crReturnV;
}
while (1) {
/*
* By this point, most incoming packets are already being
* handled by filter_queue, and we need only pay attention to
* the unusual ones.
*/
if ((pktin = ssh1_connection_pop(s)) != NULL) {
ssh_proto_error(s->ppl.ssh, "Unexpected packet received, "
"type %d (%s)", pktin->type,
ssh1_pkt_type(pktin->type));
return;
}
crReturnV;
}
crFinishV;
}
static void ssh1_channel_check_close(struct ssh1_channel *c)
{
struct ssh1_connection_state *s = c->connlayer;
PktOut *pktout;
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;
}
if ((!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes) ||
chan_want_close(c->chan, (c->closes & CLOSES_SENT_CLOSE),
(c->closes & CLOSES_RCVD_CLOSE))) &&
!(c->closes & CLOSES_SENT_CLOSECONF)) {
/*
* We have both sent and received CLOSE (or the channel type
* doesn't need us to), which means the channel is in final
* wind-up. Send CLOSE and/or CLOSE_CONFIRMATION, whichever we
* haven't sent yet.
*/
if (!(c->closes & CLOSES_SENT_CLOSE)) {
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_MSG_CHANNEL_CLOSE);
put_uint32(pktout, c->remoteid);
pq_push(s->ppl.out_pq, pktout);
c->closes |= CLOSES_SENT_CLOSE;
}
if (c->closes & CLOSES_RCVD_CLOSE) {
pktout = ssh_bpp_new_pktout(
s->ppl.bpp, SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION);
put_uint32(pktout, c->remoteid);
pq_push(s->ppl.out_pq, pktout);
c->closes |= CLOSES_SENT_CLOSECONF;
}
}
if (!((CLOSES_SENT_CLOSECONF | CLOSES_RCVD_CLOSECONF) & ~c->closes)) {
/*
* We have both sent and received CLOSE_CONFIRMATION, which
* means we're completely done with the channel.
*/
ssh1_channel_destroy(c);
}
}
static void ssh1_channel_try_eof(struct ssh1_channel *c)
{
struct ssh1_connection_state *s = c->connlayer;
PktOut *pktout;
assert(c->pending_eof); /* precondition for calling us */
if (c->halfopen)
return; /* can't close: not even opened yet */
c->pending_eof = false; /* we're about to send it */
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_MSG_CHANNEL_CLOSE);
put_uint32(pktout, c->remoteid);
pq_push(s->ppl.out_pq, pktout);
c->closes |= CLOSES_SENT_CLOSE;
ssh1_channel_check_close(c);
}
/*
* Close any local socket and free any local resources associated with
* a channel. This converts the channel into a zombie.
*/
static void ssh1_channel_close_local(struct ssh1_channel *c,
const char *reason)
{
struct ssh1_connection_state *s = c->connlayer;
PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
char *msg = chan_log_close_msg(c->chan);
if (msg != NULL) {
ppl_logevent("%s%s%s", msg, reason ? " " : "", reason ? reason : "");
sfree(msg);
}
chan_free(c->chan);
c->chan = zombiechan_new();
}
static void ssh1_check_termination_callback(void *vctx)
{
struct ssh1_connection_state *s = (struct ssh1_connection_state *)vctx;
ssh1_check_termination(s);
}
static void ssh1_channel_destroy(struct ssh1_channel *c)
{
struct ssh1_connection_state *s = c->connlayer;
ssh1_channel_close_local(c, NULL);
del234(s->channels, c);
ssh1_channel_free(c);
/*
* If that was the last channel left open, we might need to
* terminate. But we'll be a bit cautious, by doing that in a
* toplevel callback, just in case anything on the current call
* stack objects to this entire PPL being freed.
*/
queue_toplevel_callback(ssh1_check_termination_callback, s);
}
bool ssh1_check_termination(struct ssh1_connection_state *s)
{
/*
* Decide whether we should terminate the SSH connection now.
* Called after a channel goes away, or when the main session
* returns SSH1_SMSG_EXIT_STATUS; we terminate when none of either
* is left.
*/
if (s->session_terminated && count234(s->channels) == 0) {
PktOut *pktout = ssh_bpp_new_pktout(
s->ppl.bpp, SSH1_CMSG_EXIT_CONFIRMATION);
pq_push(s->ppl.out_pq, pktout);
ssh_user_close(s->ppl.ssh, "Session finished");
return true;
}
return false;
}
/*
* Set up most of a new ssh1_channel. Leaves chan untouched (since it
* will sometimes have been filled in before calling this).
*/
void ssh1_channel_init(struct ssh1_channel *c)
{
struct ssh1_connection_state *s = c->connlayer;
c->closes = 0;
c->pending_eof = false;
c->throttling_conn = false;
c->sc.vt = &ssh1channel_vtable;
c->sc.cl = &s->cl;
c->localid = alloc_channel_id(s->channels, struct ssh1_channel);
add234(s->channels, c);
}
static Conf *ssh1channel_get_conf(SshChannel *sc)
{
struct ssh1_channel *c = container_of(sc, struct ssh1_channel, sc);
struct ssh1_connection_state *s = c->connlayer;
return s->conf;
}
static void ssh1channel_write_eof(SshChannel *sc)
{
struct ssh1_channel *c = container_of(sc, struct ssh1_channel, sc);
if (c->closes & CLOSES_SENT_CLOSE)
return;
c->pending_eof = true;
ssh1_channel_try_eof(c);
}
static void ssh1channel_initiate_close(SshChannel *sc, const char *err)
{
struct ssh1_channel *c = container_of(sc, struct ssh1_channel, sc);
char *reason;
reason = err ? dupprintf("due to local error: %s", err) : NULL;
ssh1_channel_close_local(c, reason);
sfree(reason);
c->pending_eof = false; /* this will confuse a zombie channel */
ssh1_channel_check_close(c);
}
static void ssh1channel_unthrottle(SshChannel *sc, size_t bufsize)
{
struct ssh1_channel *c = container_of(sc, struct ssh1_channel, sc);
struct ssh1_connection_state *s = c->connlayer;
if (c->throttling_conn && bufsize <= SSH1_BUFFER_LIMIT) {
c->throttling_conn = false;
ssh_throttle_conn(s->ppl.ssh, -1);
}
}
static size_t ssh1channel_write(
SshChannel *sc, bool is_stderr, const void *buf, size_t len)
{
struct ssh1_channel *c = container_of(sc, struct ssh1_channel, sc);
struct ssh1_connection_state *s = c->connlayer;
assert(!(c->closes & CLOSES_SENT_CLOSE));
PktOut *pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_MSG_CHANNEL_DATA);
put_uint32(pktout, c->remoteid);
put_string(pktout, buf, len);
pq_push(s->ppl.out_pq, pktout);
/*
* 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;
}
static struct X11FakeAuth *ssh1_add_x11_display(
ConnectionLayer *cl, int authtype, struct X11Display *disp)
{
struct ssh1_connection_state *s =
container_of(cl, struct ssh1_connection_state, cl);
struct X11FakeAuth *auth = x11_invent_fake_auth(s->x11authtree, authtype);
auth->disp = disp;
return auth;
}
static SshChannel *ssh1_lportfwd_open(
ConnectionLayer *cl, const char *hostname, int port,
const char *description, const SocketPeerInfo *pi, Channel *chan)
{
struct ssh1_connection_state *s =
container_of(cl, struct ssh1_connection_state, cl);
PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
struct ssh1_channel *c = snew(struct ssh1_channel);
PktOut *pktout;
c->connlayer = s;
ssh1_channel_init(c);
c->halfopen = true;
c->chan = chan;
ppl_logevent("Opening connection to %s:%d for %s",
hostname, port, description);
pktout = ssh_bpp_new_pktout(s->ppl.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 */
pq_push(s->ppl.out_pq, pktout);
return &c->sc;
}
static void ssh1_rportfwd_remove(ConnectionLayer *cl, struct ssh_rportfwd *rpf)
{
/*
* We cannot cancel listening ports on the server side in SSH-1!
* There's no message to support it.
*/
}
static bool ssh1_agent_forwarding_permitted(ConnectionLayer *cl)
{
struct ssh1_connection_state *s =
container_of(cl, struct ssh1_connection_state, cl);
return conf_get_bool(s->conf, CONF_agentfwd) && agent_exists();
}
static void ssh1_connection_special_cmd(PacketProtocolLayer *ppl,
SessionSpecialCode code, int arg)
{
struct ssh1_connection_state *s =
container_of(ppl, struct ssh1_connection_state, ppl);
PktOut *pktout;
if (code == SS_PING || code == SS_NOP) {
if (!(s->ppl.remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) {
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_MSG_IGNORE);
put_stringz(pktout, "");
pq_push(s->ppl.out_pq, pktout);
}
} else if (s->mainchan) {
mainchan_special_cmd(s->mainchan, code, arg);
}
}
static void ssh1_terminal_size(ConnectionLayer *cl, int width, int height)
{
struct ssh1_connection_state *s =
container_of(cl, struct ssh1_connection_state, cl);
s->term_width = width;
s->term_height = height;
if (s->mainchan)
mainchan_terminal_size(s->mainchan, width, height);
}
static void ssh1_stdout_unthrottle(ConnectionLayer *cl, size_t bufsize)
{
struct ssh1_connection_state *s =
container_of(cl, struct ssh1_connection_state, cl);
if (s->stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) {
s->stdout_throttling = false;
ssh_throttle_conn(s->ppl.ssh, -1);
}
}
static size_t ssh1_stdin_backlog(ConnectionLayer *cl)
{
return 0;
}
static void ssh1_throttle_all_channels(ConnectionLayer *cl, bool throttled)
{
struct ssh1_connection_state *s =
container_of(cl, struct ssh1_connection_state, cl);
struct ssh1_channel *c;
int i;
for (i = 0; NULL != (c = index234(s->channels, i)); i++)
chan_set_input_wanted(c->chan, !throttled);
}
static bool ssh1_ldisc_option(ConnectionLayer *cl, int option)
{
struct ssh1_connection_state *s =
container_of(cl, struct ssh1_connection_state, cl);
return s->ldisc_opts[option];
}
static void ssh1_set_ldisc_option(ConnectionLayer *cl, int option, bool value)
{
struct ssh1_connection_state *s =
container_of(cl, struct ssh1_connection_state, cl);
s->ldisc_opts[option] = value;
}
static void ssh1_enable_x_fwd(ConnectionLayer *cl)
{
struct ssh1_connection_state *s =
container_of(cl, struct ssh1_connection_state, cl);
s->X11_fwd_enabled = true;
}
static void ssh1_set_wants_user_input(ConnectionLayer *cl, bool wanted)
{
struct ssh1_connection_state *s =
container_of(cl, struct ssh1_connection_state, cl);
s->want_user_input = wanted;
s->finished_setup = true;
}
static bool ssh1_connection_want_user_input(PacketProtocolLayer *ppl)
{
struct ssh1_connection_state *s =
container_of(ppl, struct ssh1_connection_state, ppl);
return s->want_user_input;
}
static void ssh1_connection_got_user_input(PacketProtocolLayer *ppl)
{
struct ssh1_connection_state *s =
container_of(ppl, struct ssh1_connection_state, ppl);
while (s->mainchan && bufchain_size(s->ppl.user_input) > 0) {
/*
* Add user input to the main channel's buffer.
*/
ptrlen data = bufchain_prefix(s->ppl.user_input);
if (data.len > 512)
data.len = 512;
sshfwd_write(&s->mainchan_sc, data.ptr, data.len);
bufchain_consume(s->ppl.user_input, data.len);
}
}
static void ssh1_connection_reconfigure(PacketProtocolLayer *ppl, Conf *conf)
{
struct ssh1_connection_state *s =
container_of(ppl, struct ssh1_connection_state, ppl);
conf_free(s->conf);
s->conf = conf_copy(conf);
if (s->portfwdmgr_configured)
portfwdmgr_config(s->portfwdmgr, s->conf);
}