1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-07-05 21:42:47 -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

68
ssh.c
View File

@ -474,8 +474,7 @@ struct ssh_tag {
int incoming_data_seen_eof;
char *incoming_data_eof_message;
PktInQueue pq_full;
struct IdempotentCallback pq_full_consumer;
struct IdempotentCallback incoming_pkt_consumer;
PktInQueue pq_ssh1_login;
struct IdempotentCallback ssh1_login_icb;
@ -725,22 +724,7 @@ static int s_write(Ssh ssh, const void *data, int len)
static void ssh_pkt_write(Ssh ssh, PktOut *pkt)
{
if (ssh->version == 2 && ssh->v2_cbc_ignore_workaround &&
bufchain_size(&ssh->outgoing_data) != 0) {
/*
* 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 prefixing
* each packet that might contain user data with an
* SSH_MSG_IGNORE.
*/
PktOut *ipkt = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_IGNORE);
put_stringz(ipkt, "");
ssh_bpp_format_packet(ssh->bpp, ipkt);
}
ssh_bpp_format_packet(ssh->bpp, pkt);
queue_idempotent_callback(&ssh->outgoing_data_sender);
pq_push(&ssh->bpp->out_pq, pkt);
}
/*
@ -881,8 +865,6 @@ static void ssh2_add_sigblob(Ssh ssh, PktOut *pkt,
static void ssh_feed_to_bpp(Ssh ssh)
{
PacketQueueNode *prev_tail = ssh->pq_full.pqb.end.prev;
assert(ssh->bpp);
ssh_bpp_handle_input(ssh->bpp);
@ -904,9 +886,6 @@ static void ssh_feed_to_bpp(Ssh ssh)
ssh->close_expected = TRUE;
ssh->disconnect_message_seen = TRUE;
}
if (ssh->pq_full.pqb.end.prev != prev_tail)
queue_idempotent_callback(&ssh->pq_full_consumer);
}
static void ssh_got_ssh_version(struct ssh_version_receiver *rcv,
@ -976,12 +955,14 @@ static void ssh_got_ssh_version(struct ssh_version_receiver *rcv,
}
ssh->bpp->out_raw = &ssh->outgoing_data;
ssh->bpp->out_raw->ic = &ssh->outgoing_data_sender;
ssh->bpp->in_raw = &ssh->incoming_data;
ssh->bpp->in_pq = &ssh->pq_full;
ssh->bpp->in_pq.pqb.ic = &ssh->incoming_pkt_consumer;
ssh->bpp->pls = &ssh->pls;
ssh->bpp->logctx = ssh->logctx;
ssh->bpp->remote_bugs = ssh->remote_bugs;
queue_idempotent_callback(&ssh->incoming_data_consumer);
queue_idempotent_callback(&ssh->bpp->ic_in_raw);
queue_idempotent_callback(&ssh->user_input_consumer);
update_specials_menu(ssh->frontend);
@ -1034,12 +1015,12 @@ static void ssh_process_incoming_data(void *ctx)
}
}
static void ssh_process_pq_full(void *ctx)
static void ssh_process_incoming_pkts(void *ctx)
{
Ssh ssh = (Ssh)ctx;
PktIn *pktin;
while ((pktin = pq_pop(&ssh->pq_full)) != NULL) {
while ((pktin = pq_pop(&ssh->bpp->in_pq)) != NULL) {
if (ssh->general_packet_processing)
ssh->general_packet_processing(ssh, pktin);
ssh->packet_dispatch[pktin->type](ssh, pktin);
@ -1730,6 +1711,13 @@ static void do_ssh1_login(void *vctx)
sfree(s->rsabuf);
/*
* Force the BPP to synchronously marshal all packets up to and
* including the SESSION_KEY into wire format, before we turn on
* crypto.
*/
ssh_bpp_handle_output(ssh->bpp);
{
const struct ssh1_cipheralg *cipher =
(s->cipher_type == SSH_CIPHER_BLOWFISH ? &ssh1_blowfish :
@ -5077,6 +5065,13 @@ static void do_ssh2_transport(void *vctx)
ssh->stats.out.running = TRUE;
ssh->stats.out.remaining = ssh->max_data_size;
/*
* Force the BPP to synchronously marshal all packets up to and
* including that NEWKEYS into wire format, before we switch over
* to new crypto.
*/
ssh_bpp_handle_output(ssh->bpp);
/*
* We've sent client NEWKEYS, so create and initialise
* client-to-server session keys.
@ -6616,10 +6611,10 @@ static void ssh2_msg_userauth(Ssh ssh, PktIn *pktin)
* protocol has officially started, which means we must
* install the dispatch-table entries for all the
* connection-layer messages. In particular, we must do this
* _before_ we return to the loop in ssh_process_pq_full
* _before_ we return to the loop in ssh_process_incoming_pkts
* that's processing the currently queued packets through the
* dispatch table, because if (say) an SSH_MSG_GLOBAL_REQUEST
* is already pending in pq_full, we can't afford to delay
* is already pending in in_pq, we can't afford to delay
* installing its dispatch table entry until after that queue
* run is done.
*/
@ -8331,14 +8326,15 @@ static void do_ssh2_connection(void *vctx)
/*
* Put our current pending packet queue back to the front of
* pq_full, and then schedule a callback to re-process those
* the main pq, and then schedule a callback to re-process those
* packets (if any). That way, anything already in our queue that
* matches any of the table entries we've just modified will go to
* the right handler function, and won't come here to confuse us.
*/
if (pq_peek(&ssh->pq_ssh2_connection)) {
pq_concatenate(&ssh->pq_full, &ssh->pq_ssh2_connection, &ssh->pq_full);
queue_idempotent_callback(&ssh->pq_full_consumer);
pq_concatenate(&ssh->bpp->in_pq,
&ssh->pq_ssh2_connection, &ssh->bpp->in_pq);
queue_idempotent_callback(ssh->bpp->in_pq.pqb.ic);
}
/*
@ -9005,10 +9001,9 @@ static const char *ssh_init(Frontend *frontend, Backend **backend_handle,
ssh->incoming_data_consumer.fn = ssh_process_incoming_data;
ssh->incoming_data_consumer.ctx = ssh;
ssh->incoming_data_consumer.queued = FALSE;
pq_in_init(&ssh->pq_full);
ssh->pq_full_consumer.fn = ssh_process_pq_full;
ssh->pq_full_consumer.ctx = ssh;
ssh->pq_full_consumer.queued = FALSE;
ssh->incoming_pkt_consumer.fn = ssh_process_incoming_pkts;
ssh->incoming_pkt_consumer.ctx = ssh;
ssh->incoming_pkt_consumer.queued = FALSE;
pq_in_init(&ssh->pq_ssh1_login);
ssh->ssh1_login_icb.fn = do_ssh1_login;
ssh->ssh1_login_icb.ctx = ssh;
@ -9191,7 +9186,6 @@ static void ssh_free(Backend *be)
bufchain_clear(&ssh->incoming_data);
bufchain_clear(&ssh->outgoing_data);
sfree(ssh->incoming_data_eof_message);
pq_in_clear(&ssh->pq_full);
pq_in_clear(&ssh->pq_ssh1_login);
pq_in_clear(&ssh->pq_ssh1_connection);
pq_in_clear(&ssh->pq_ssh2_transport);