1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-07-14 01:27:35 -05:00

Give the BPP an input and output packet queue.

Now, instead of writing each packet straight on to the raw output
bufchain by calling the BPP's format_packet function, the higher
protocol layers will put the packets on to a queue, which will
automatically trigger a callback (using the new mechanism for
embedding a callback in any packet queue) to make the BPP format its
queue on to the raw-output bufchain. That in turn triggers a second
callback which moves the data to the socket.

This means in particular that the CBC ignore-message workaround can be
moved into the new BPP routine to process the output queue, which is a
good place for it because then it can easily arrange to only put an
ignore message at the start of any sequence of packets that are being
formatted as a single output blob.
This commit is contained in:
Simon Tatham
2018-09-24 18:08:09 +01:00
parent 60d95b6a62
commit 6bb847738b
7 changed files with 173 additions and 72 deletions

View File

@ -25,6 +25,7 @@ struct ssh2_bpp_state {
unsigned cipherblk;
PktIn *pktin;
struct DataTransferStats *stats;
int cbc_ignore_workaround;
struct ssh2_bpp_direction in, out;
/* comp and decomp logically belong in the per-direction
@ -39,14 +40,14 @@ struct ssh2_bpp_state {
static void ssh2_bpp_free(BinaryPacketProtocol *bpp);
static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp);
static void ssh2_bpp_handle_output(BinaryPacketProtocol *bpp);
static PktOut *ssh2_bpp_new_pktout(int type);
static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt);
static const struct BinaryPacketProtocolVtable ssh2_bpp_vtable = {
ssh2_bpp_free,
ssh2_bpp_handle_input,
ssh2_bpp_handle_output,
ssh2_bpp_new_pktout,
ssh2_bpp_format_packet,
};
BinaryPacketProtocol *ssh2_bpp_new(struct DataTransferStats *stats)
@ -55,6 +56,7 @@ BinaryPacketProtocol *ssh2_bpp_new(struct DataTransferStats *stats)
memset(s, 0, sizeof(*s));
s->bpp.vt = &ssh2_bpp_vtable;
s->stats = stats;
ssh_bpp_common_setup(&s->bpp);
return &s->bpp;
}
@ -99,8 +101,13 @@ void ssh2_bpp_new_outgoing_crypto(
s->out.cipher = ssh2_cipher_new(cipher);
ssh2_cipher_setkey(s->out.cipher, ckey);
ssh2_cipher_setiv(s->out.cipher, iv);
s->cbc_ignore_workaround = (
(ssh2_cipher_alg(s->out.cipher)->flags & SSH_CIPHER_IS_CBC) &&
!(s->bpp.remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE));
} else {
s->out.cipher = NULL;
s->cbc_ignore_workaround = FALSE;
}
s->out.etm_mode = etm_mode;
if (mac) {
@ -478,7 +485,7 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
continue;
}
pq_push(s->bpp.in_pq, s->pktin);
pq_push(&s->bpp.in_pq, s->pktin);
{
int type = s->pktin->type;
@ -611,10 +618,8 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
}
static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
static void ssh2_bpp_format_packet(struct ssh2_bpp_state *s, PktOut *pkt)
{
struct ssh2_bpp_state *s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
if (pkt->minlen > 0 && !s->out_comp) {
/*
* If we've been told to pad the packet out to a given minimum
@ -678,6 +683,40 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
ssh2_bpp_format_packet_inner(s, pkt);
bufchain_add(s->bpp.out_raw, pkt->data, pkt->length);
ssh_free_pktout(pkt);
}
static void ssh2_bpp_handle_output(BinaryPacketProtocol *bpp)
{
struct ssh2_bpp_state *s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
PktOut *pkt;
if (s->cbc_ignore_workaround) {
/*
* When using a CBC-mode cipher in SSH-2, it's necessary to
* ensure that an attacker can't provide data to be encrypted
* using an IV that they know. We ensure this by inserting an
* SSH_MSG_IGNORE if the last cipher block of the previous
* packet has already been sent to the network (which we
* approximate conservatively by checking if it's vanished
* from out_raw).
*/
if (bufchain_size(s->bpp.out_raw) <
(ssh2_cipher_alg(s->out.cipher)->blksize +
ssh2_mac_alg(s->out.mac)->len)) {
/*
* There's less data in out_raw than the MAC size plus the
* cipher block size, which means at least one byte of
* that cipher block must already have left. Add an
* IGNORE.
*/
pkt = ssh_bpp_new_pktout(&s->bpp, SSH2_MSG_IGNORE);
put_stringz(pkt, "");
ssh2_bpp_format_packet(s, pkt);
}
}
while ((pkt = pq_pop(&s->bpp.out_pq)) != NULL) {
ssh2_bpp_format_packet(s, pkt);
ssh_free_pktout(pkt);
}
}