📄 ssh.c
字号:
CHAN_SOCKDATA,
CHAN_SOCKDATA_DORMANT /* one the remote hasn't confirmed */
};
/*
* 2-3-4 tree storing channels.
*/
struct ssh_channel {
Ssh ssh; /* pointer back to main context */
unsigned remoteid, localid;
int type;
/*
* In SSH1, this value contains four bits:
*
* 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.
*/
int closes;
union {
struct ssh1_data_channel {
int throttling;
} v1;
struct ssh2_data_channel {
bufchain outbuffer;
unsigned remwindow, remmaxpkt;
unsigned locwindow;
} v2;
} v;
union {
struct ssh_agent_channel {
unsigned char *message;
unsigned char msglen[4];
int lensofar, totallen;
} a;
struct ssh_x11_channel {
Socket s;
} x11;
struct ssh_pfd_channel {
Socket s;
} pfd;
} u;
};
/*
* 2-3-4 tree storing remote->local port forwardings. SSH 1 and SSH
* 2 use this structure in different ways, reflecting SSH 2's
* altogether saner approach to port forwarding.
*
* In SSH 1, you arrange a remote forwarding by sending the server
* 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.
*
* In SSH 2, each side of the connection minds its own business and
* 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.
*
* Hence: in SSH 1 this structure stores host:port pairs we intend
* to allow connections to, and is indexed by those host:port
* pairs. In SSH 2 it stores a mapping from source port to
* destination host:port pair, and is indexed by source port.
*/
struct ssh_rportfwd {
unsigned sport, dport;
char dhost[256];
};
struct Packet {
long length;
int type;
unsigned char *data;
unsigned char *body;
long savedpos;
long maxlen;
};
static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt);
static void ssh2_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt);
static void ssh_size(void *handle, int width, int height);
static void ssh_special(void *handle, Telnet_Special);
static int ssh2_try_send(struct ssh_channel *c);
static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, int len);
static void ssh_throttle_all(Ssh ssh, int enable, int bufsize);
static void ssh2_set_window(struct ssh_channel *c, unsigned newwin);
static int ssh_sendbuffer(void *handle);
static void ssh_do_close(Ssh ssh);
struct rdpkt1_state_tag {
long len, pad, biglen, to_read;
unsigned long realcrc, gotcrc;
unsigned char *p;
int i;
int chunk;
};
struct rdpkt2_state_tag {
long len, pad, payload, packetlen, maclen;
int i;
int cipherblk;
unsigned long incoming_sequence;
};
struct ssh_tag {
const struct plug_function_table *fn;
/* the above field _must_ be first in the structure */
SHA_State exhash, exhashbase;
Socket s;
void *ldisc;
void *logctx;
unsigned char session_key[32];
int v1_compressing;
int v1_remote_protoflags;
int v1_local_protoflags;
int agentfwd_enabled;
int X11_fwd_enabled;
int remote_bugs;
const struct ssh_cipher *cipher;
void *v1_cipher_ctx;
void *crcda_ctx;
const struct ssh2_cipher *cscipher, *sccipher;
void *cs_cipher_ctx, *sc_cipher_ctx;
const struct ssh_mac *csmac, *scmac;
void *cs_mac_ctx, *sc_mac_ctx;
const struct ssh_compress *cscomp, *sccomp;
void *cs_comp_ctx, *sc_comp_ctx;
const struct ssh_kex *kex;
const struct ssh_signkey *hostkey;
unsigned char v2_session_id[20];
void *kex_ctx;
char *savedhost;
int savedport;
int send_ok;
int echoing, editing;
void *frontend;
int ospeed, ispeed; /* temporaries */
int term_width, term_height;
tree234 *channels; /* indexed by local id */
struct ssh_channel *mainchan; /* primary session channel */
int exitcode;
tree234 *rportfwds;
enum {
SSH_STATE_PREPACKET,
SSH_STATE_BEFORE_SIZE,
SSH_STATE_INTERMED,
SSH_STATE_SESSION,
SSH_STATE_CLOSED
} state;
int size_needed, eof_needed;
struct Packet pktin;
struct Packet pktout;
unsigned char *deferred_send_data;
int deferred_len, deferred_size;
/*
* 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;
/*
* Used for username and password input.
*/
char *userpass_input_buffer;
int userpass_input_buflen;
int userpass_input_bufpos;
int userpass_input_echo;
char *portfwd_strptr;
int pkt_ctx;
void *x11auth;
int version;
int v1_throttle_count;
int overall_bufsize;
int throttled_all;
int v1_stdout_throttling;
int v2_outgoing_sequence;
int ssh1_rdpkt_crstate;
int ssh2_rdpkt_crstate;
int do_ssh_init_crstate;
int ssh_gotdata_crstate;
int ssh1_protocol_crstate;
int do_ssh1_login_crstate;
int do_ssh2_transport_crstate;
int do_ssh2_authconn_crstate;
void *do_ssh_init_state;
void *do_ssh1_login_state;
void *do_ssh2_transport_state;
void *do_ssh2_authconn_state;
struct rdpkt1_state_tag rdpkt1_state;
struct rdpkt2_state_tag rdpkt2_state;
void (*protocol) (Ssh ssh, unsigned char *in, int inlen, int ispkt);
int (*s_rdpkt) (Ssh ssh, unsigned char **data, int *datalen);
/*
* We maintain a full _copy_ of a Config structure here, not
* merely a pointer to it. 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.
*/
Config cfg;
/*
* Used to transfer data back from async agent callbacks.
*/
void *agent_response;
int agent_response_len;
};
#define logevent(s) logevent(ssh->frontend, s)
/* logevent, only printf-formatted. */
static void logeventf(Ssh ssh, const char *fmt, ...)
{
va_list ap;
char *buf;
va_start(ap, fmt);
buf = dupvprintf(fmt, ap);
va_end(ap);
logevent(buf);
sfree(buf);
}
#define bombout(msg) \
do { \
char *text = dupprintf msg; \
ssh_do_close(ssh); \
logevent(text); \
connection_fatal(ssh->frontend, "%s", text); \
sfree(text); \
} while (0)
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;
return 0;
}
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;
return 0;
}
static int ssh_rportcmp_ssh1(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 int ssh_rportcmp_ssh2(void *av, void *bv)
{
struct ssh_rportfwd *a = (struct ssh_rportfwd *) av;
struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv;
if (a->sport > b->sport)
return +1;
if (a->sport < b->sport)
return -1;
return 0;
}
static int alloc_channel_id(Ssh ssh)
{
const unsigned CHANNEL_NUMBER_OFFSET = 256;
unsigned low, high, mid;
int tsize;
struct ssh_channel *c;
/*
* First-fit allocation of channel numbers: always pick the
* lowest unused one. To do this, binary-search using the
* counted B-tree to find the largest channel ID which is in a
* contiguous sequence from the beginning. (Precisely
* everything in that sequence must have ID equal to its tree
* index plus CHANNEL_NUMBER_OFFSET.)
*/
tsize = count234(ssh->channels);
low = -1;
high = tsize;
while (high - low > 1) {
mid = (high + low) / 2;
c = index234(ssh->channels, mid);
if (c->localid == mid + CHANNEL_NUMBER_OFFSET)
low = mid; /* this one is fine */
else
high = mid; /* this one is past it */
}
/*
* Now low points to either -1, or the tree index of the
* largest ID in the initial sequence.
*/
{
unsigned i = low + 1 + CHANNEL_NUMBER_OFFSET;
assert(NULL == find234(ssh->channels, &i, ssh_channelfind));
}
return low + 1 + CHANNEL_NUMBER_OFFSET;
}
static void c_write(Ssh ssh, const char *buf, int len)
{
if ((flags & FLAG_STDERR)) {
int i;
for (i = 0; i < len; i++)
if (buf[i] != '\r')
fputc(buf[i], stderr);
return;
}
from_backend(ssh->frontend, 1, buf, len);
}
static void c_write_untrusted(Ssh ssh, const char *buf, int len)
{
int i;
for (i = 0; i < len; i++) {
if (buf[i] == '\n')
c_write(ssh, "\r\n", 2);
else if ((buf[i] & 0x60) || (buf[i] == '\r'))
c_write(ssh, buf + i, 1);
}
}
static void c_write_str(Ssh ssh, const char *buf)
{
c_write(ssh, buf, strlen(buf));
}
/*
* Collect incoming data in the incoming packet buffer.
* Decipher and verify the packet when it is completely read.
* Drop SSH1_MSG_DEBUG and SSH1_MSG_IGNORE packets.
* Update the *data and *datalen variables.
* Return the additional nr of bytes needed, or 0 when
* a complete packet is available.
*/
static int ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
{
struct rdpkt1_state_tag *st = &ssh->rdpkt1_state;
crBegin(ssh->ssh1_rdpkt_crstate);
next_packet:
ssh->pktin.type = 0;
ssh->pktin.length = 0;
for (st->i = st->len = 0; st->i < 4; st->i++) {
while ((*datalen) == 0)
crReturn(4 - st->i);
st->len = (st->len << 8) + **data;
(*data)++, (*datalen)--;
}
st->pad = 8 - (st->len % 8);
st->biglen = st->len + st->pad;
ssh->pktin.length = st->len - 5;
if (ssh->pktin.maxlen < st->biglen) {
ssh->pktin.maxlen = st->biglen;
ssh->pktin.data = sresize(ssh->pktin.data, st->biglen + APIEXTRA,
unsigned char);
}
st->to_read = st->biglen;
st->p = ssh->pktin.data;
while (st->to_read > 0) {
st->chunk = st->to_read;
while ((*datalen) == 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -