📄 ssh.c
字号:
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(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));
}
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->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];
/* Not used in FileZilla
/*
* 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) ||
(st->pktin->type == SSH1_SMSG_STDERR_DATA)) {
do_blank = TRUE; blank_prefix = 0;
} else if (st->pktin->type == SSH1_MSG_CHANNEL_DATA) {
do_blank = TRUE; blank_prefix = 4;
}
if (do_blank) {
blank.offset = blank_prefix;
blank.len = st->pktin->length;
blank.type = PKTLOG_OMIT;
nblanks = 1;
}
}
log_packet(ssh->logctx,
PKT_INCOMING, st->pktin->type,
ssh1_pkt_type(st->pktin->type),
st->pktin->body, st->pktin->length,
nblanks, &blank);
}*/
crFinish(st->pktin);
}
static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
{
struct rdpkt2_state_tag *st = &ssh->rdpkt2_state;
crBegin(ssh->ssh2_rdpkt_crstate);
st->pktin = ssh_new_packet();
st->pktin->type = 0;
st->pktin->length = 0;
if (ssh->sccipher)
st->cipherblk = ssh->sccipher->blksize;
else
st->cipherblk = 8;
if (st->cipherblk < 8)
st->cipherblk = 8;
st->pktin->data = snewn(st->cipherblk + APIEXTRA, unsigned char);
/*
* Acquire and decrypt the first block of the packet. This will
* contain the length and padding details.
*/
for (st->i = st->len = 0; st->i < st->cipherblk; st->i++) {
while ((*datalen) == 0)
crReturn(NULL);
st->pktin->data[st->i] = *(*data)++;
(*datalen)--;
}
if (ssh->sccipher)
ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
st->pktin->data, st->cipherblk);
/*
* Now get the length and padding figures.
*/
st->len = GET_32BIT(st->pktin->data);
st->pad = st->pktin->data[4];
/*
* _Completely_ silly lengths should be stomped on before they
* do us any more damage.
*/
if (st->len < 0 || st->pad < 0 || st->len + st->pad < 0) {
bombout(("Incoming packet was garbled on decryption"));
ssh_free_packet(st->pktin);
crStop(NULL);
}
/*
* This enables us to deduce the payload length.
*/
st->payload = st->len - st->pad - 1;
st->pktin->length = st->payload + 5;
/*
* So now we can work out the total packet length.
*/
st->packetlen = st->len + 4;
st->maclen = ssh->scmac ? ssh->scmac->len : 0;
/*
* Allocate memory for the rest of the packet.
*/
st->pktin->maxlen = st->packetlen + st->maclen;
st->pktin->data = sresize(st->pktin->data,
st->pktin->maxlen + APIEXTRA,
unsigned char);
/*
* Read and decrypt the remainder of the packet.
*/
for (st->i = st->cipherblk; st->i < st->packetlen + st->maclen;
st->i++) {
while ((*datalen) == 0)
crReturn(NULL);
st->pktin->data[st->i] = *(*data)++;
(*datalen)--;
}
/* Decrypt everything _except_ the MAC. */
if (ssh->sccipher)
ssh->sccipher->decrypt(ssh->sc_cipher_ctx,
st->pktin->data + st->cipherblk,
st->packetlen - st->cipherblk);
st->pktin->encrypted_len = st->packetlen;
/*
* Check the MAC.
*/
if (ssh->scmac
&& !ssh->scmac->verify(ssh->sc_mac_ctx, st->pktin->data, st->len + 4,
st->incoming_sequence)) {
bombout(("Incorrect MAC received on packet"));
ssh_free_packet(st->pktin);
crStop(NULL);
}
st->pktin->sequence = st->incoming_sequence++;
/*
* Decompress packet payload.
*/
{
unsigned char *newpayload;
int newlen;
if (ssh->sccomp &&
ssh->sccomp->decompress(ssh->sc_comp_ctx,
st->pktin->data + 5, st->pktin->length - 5,
&newpayload, &newlen)) {
if (st->pktin->maxlen < newlen + 5) {
st->pktin->maxlen = newlen + 5;
st->pktin->data = sresize(st->pktin->data,
st->pktin->maxlen + APIEXTRA,
unsigned char);
}
st->pktin->length = 5 + newlen;
memcpy(st->pktin->data + 5, newpayload, newlen);
sfree(newpayload);
}
}
st->pktin->savedpos = 6;
st->pktin->body = st->pktin->data;
st->pktin->type = st->pktin->data[5];
/* Not used in FileZilla
/*
* 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 == SSH2_MSG_CHANNEL_DATA) {
do_blank = TRUE; blank_prefix = 4;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -