📄 ssh.c
字号:
Config cfg;
/*
* Used to transfer data back from async callbacks.
*/
void *agent_response;
int agent_response_len;
int user_response;
/*
* The SSH connection can be set as `frozen', meaning we are
* not currently accepting incoming data from the network. This
* is slightly more serious than setting the _socket_ as
* frozen, because we may already have had data passed to us
* from the network which we need to delay processing until
* after the freeze is lifted, so we also need a bufchain to
* store that data.
*/
int frozen;
bufchain queued_incoming_data;
/*
* Dispatch table for packet types that we may have to deal
* with at any time.
*/
handler_fn_t packet_dispatch[256];
/*
* Queues of one-off handler functions for success/failure
* indications from a request.
*/
struct queued_handler *qhead, *qtail;
/*
* This module deals with sending keepalives.
*/
Pinger pinger;
/*
* Track incoming and outgoing data sizes and time, for
* size-based rekeys.
*/
unsigned long incoming_data_size, outgoing_data_size, deferred_data_size;
unsigned long max_data_size;
int kex_in_progress;
long next_rekey, last_rekey;
char *deferred_rekey_reason; /* points to STATIC string; don't free */
};
#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, FALSE); \
logevent(text); \
connection_fatal(ssh->frontend, "%s", text); \
sfree(text); \
} while (0)
/* Functions to leave bits out of the SSH packet log file. */
static void dont_log_password(Ssh ssh, struct Packet *pkt, int blanktype)
{
if (ssh->cfg.logomitpass)
pkt->logmode = blanktype;
}
static void dont_log_data(Ssh ssh, struct Packet *pkt, int blanktype)
{
if (ssh->cfg.logomitdata)
pkt->logmode = blanktype;
}
static void end_log_omission(Ssh ssh, struct Packet *pkt)
{
pkt->logmode = PKTLOG_EMIT;
}
/* Helper function for common bits of parsing cfg.ttymodes. */
static void parse_ttymodes(Ssh ssh, char *modes,
void (*do_mode)(void *data, char *mode, char *val),
void *data)
{
while (*modes) {
char *t = strchr(modes, '\t');
char *m = snewn(t-modes+1, char);
char *val;
strncpy(m, modes, t-modes);
m[t-modes] = '\0';
if (*(t+1) == 'A')
val = get_ttymode(ssh->frontend, m);
else
val = dupstr(t+2);
if (val)
do_mode(data, m, val);
sfree(m);
sfree(val);
modes += strlen(modes) + 1;
}
}
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;
}
/*
* Special form of strcmp which can cope with NULL inputs. NULL is
* defined to sort before even the empty string.
*/
static int nullstrcmp(const char *a, const char *b)
{
if (a == NULL && b == NULL)
return 0;
if (a == NULL)
return -1;
if (b == NULL)
return +1;
return strcmp(a, b);
}
static int ssh_portcmp(void *av, void *bv)
{
struct ssh_portfwd *a = (struct ssh_portfwd *) av;
struct ssh_portfwd *b = (struct ssh_portfwd *) bv;
int i;
if (a->type > b->type)
return +1;
if (a->type < b->type)
return -1;
if (a->addressfamily > b->addressfamily)
return +1;
if (a->addressfamily < b->addressfamily)
return -1;
if ( (i = nullstrcmp(a->saddr, b->saddr)) != 0)
return i < 0 ? -1 : +1;
if (a->sport > b->sport)
return +1;
if (a->sport < b->sport)
return -1;
if (a->type != 'D') {
if ( (i = nullstrcmp(a->daddr, b->daddr)) != 0)
return i < 0 ? -1 : +1;
if (a->dport > b->dport)
return +1;
if (a->dport < b->dport)
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_stderr(int trusted, const char *buf, int len)
{
int i;
for (i = 0; i < len; i++)
if (buf[i] != '\r' && (trusted || buf[i] == '\n' || (buf[i] & 0x60)))
fputc(buf[i], stderr);
}
static void c_write(Ssh ssh, const char *buf, int len)
{
if (flags & FLAG_STDERR)
c_write_stderr(1, buf, len);
else
from_backend(ssh->frontend, 1, buf, len);
}
static void c_write_untrusted(Ssh ssh, const char *buf, int len)
{
if (flags & FLAG_STDERR)
c_write_stderr(0, buf, len);
else
from_backend_untrusted(ssh->frontend, buf, len);
}
static void c_write_str(Ssh ssh, const char *buf)
{
c_write(ssh, buf, strlen(buf));
}
static void ssh_free_packet(struct Packet *pkt)
{
sfree(pkt->data);
sfree(pkt);
}
static struct Packet *ssh_new_packet(void)
{
struct Packet *pkt = snew(struct Packet);
pkt->body = pkt->data = NULL;
pkt->maxlen = 0;
pkt->logmode = PKTLOG_EMIT;
pkt->nblanks = 0;
pkt->blanks = NULL;
return pkt;
}
/*
* 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 a Packet structure when a packet is completed.
*/
static struct Packet *ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
{
struct rdpkt1_state_tag *st = &ssh->rdpkt1_state;
crBegin(ssh->ssh1_rdpkt_crstate);
st->pktin = ssh_new_packet();
st->pktin->type = 0;
st->pktin->length = 0;
for (st->i = st->len = 0; st->i < 4; st->i++) {
while ((*datalen) == 0)
crReturn(NULL);
st->len = (st->len << 8) + **data;
(*data)++, (*datalen)--;
}
st->pad = 8 - (st->len % 8);
st->biglen = st->len + st->pad;
st->pktin->length = st->len - 5;
if (st->biglen < 0) {
bombout(("Extremely large packet length from server suggests"
" data stream corruption"));
ssh_free_packet(st->pktin);
crStop(NULL);
}
st->pktin->maxlen = st->biglen;
st->pktin->data = snewn(st->biglen + APIEXTRA, unsigned char);
st->to_read = st->biglen;
st->p = st->pktin->data;
while (st->to_read > 0) {
st->chunk = st->to_read;
while ((*datalen) == 0)
crReturn(NULL);
if (st->chunk > (*datalen))
st->chunk = (*datalen);
memcpy(st->p, *data, st->chunk);
*data += st->chunk;
*datalen -= st->chunk;
st->p += st->chunk;
st->to_read -= st->chunk;
}
if (ssh->cipher && detect_attack(ssh->crcda_ctx, st->pktin->data,
st->biglen, NULL)) {
bombout(("Network attack (CRC compensation) detected!"));
ssh_free_packet(st->pktin);
crStop(NULL);
}
if (ssh->cipher)
ssh->cipher->decrypt(ssh->v1_cipher_ctx, st->pktin->data, st->biglen);
st->realcrc = crc32_compute(st->pktin->data, st->biglen - 4);
st->gotcrc = GET_32BIT(st->pktin->data + st->biglen - 4);
if (st->gotcrc != st->realcrc) {
bombout(("Incorrect CRC received on packet"));
ssh_free_packet(st->pktin);
crStop(NULL);
}
st->pktin->body = st->pktin->data + st->pad + 1;
st->pktin->savedpos = 0;
if (ssh->v1_compressing) {
unsigned char *decompblk;
int decomplen;
if (!zlib_decompress_block(ssh->sc_comp_ctx,
st->pktin->body - 1, st->pktin->length + 1,
&decompblk, &decomplen)) {
bombout(("Zlib decompression encountered invalid data"));
ssh_free_packet(st->pktin);
crStop(NULL);
}
if (st->pktin->maxlen < st->pad + decomplen) {
st->pktin->maxlen = st->pad + decomplen;
st->pktin->data = sresize(st->pktin->data,
st->pktin->maxlen + APIEXTRA,
unsigned char);
st->pktin->body = st->pktin->data + st->pad + 1;
}
memcpy(st->pktin->body - 1, decompblk, decomplen);
sfree(decompblk);
st->pktin->length = decomplen - 1;
}
st->pktin->type = st->pktin->body[-1];
/*
* Log incoming packet, possibly omitting sensitive fields.
*/
if (ssh->logctx) {
int nblanks = 0;
struct logblank_t blank;
if (ssh->cfg.logomitdata) {
int do_blank = FALSE, blank_prefix = 0;
/* "Session data" packets - omit the data field */
if ((st->pktin->type == SSH1_SMSG_STDOUT_DATA) ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -