1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-07-05 21:42:47 -05:00

New system for tracking data-limit-based rekeys.

I've removed the encrypted_len fields from PktIn and PktOut, which
were used to communicate from the BPP to ssh.c how much each packet
contributed to the amount of data encrypted with a given set of cipher
keys. It seems more sensible to have the BPP itself keep that counter
- especially since only one of the three BPPs even needs to count it
at all. So now there's a new DataTransferStats structure which the BPP
updates, and ssh.c only needs to check it for overflow and reset the
limits.
This commit is contained in:
Simon Tatham
2018-09-19 21:28:21 +01:00
parent 3ad919f9e9
commit 93f2df9b83
5 changed files with 65 additions and 25 deletions

49
ssh.c
View File

@ -559,8 +559,8 @@ struct ssh_tag {
* Track incoming and outgoing data sizes and time, for
* size-based rekeys.
*/
unsigned long incoming_data_size, outgoing_data_size;
unsigned long max_data_size;
struct DataTransferStats stats;
int kex_in_progress;
unsigned long next_rekey, last_rekey;
const char *deferred_rekey_reason;
@ -781,10 +781,9 @@ static void ssh_send_outgoing_data(void *ctx)
backlog = s_write(ssh, data, len);
bufchain_consume(&ssh->outgoing_data, len);
ssh->outgoing_data_size += len;
if (ssh->version == 2 && !ssh->kex_in_progress &&
!ssh->bare_connection && ssh->max_data_size != 0 &&
ssh->outgoing_data_size > ssh->max_data_size) {
ssh->state != SSH_STATE_PREPACKET &&
!ssh->bare_connection && !ssh->stats.out.running) {
ssh->rekey_reason = "too much data sent";
ssh->rekey_class = RK_NORMAL;
queue_idempotent_callback(&ssh->ssh2_transport_icb);
@ -5131,7 +5130,9 @@ static void do_ssh2_transport(void *vctx)
*/
s->pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_NEWKEYS);
ssh_pkt_write(ssh, s->pktout);
ssh->outgoing_data_size = 0; /* start counting from here */
/* Start counting down the outgoing-data limit for these cipher keys. */
ssh->stats.out.running = TRUE;
ssh->stats.out.remaining = ssh->max_data_size;
/*
* We've sent client NEWKEYS, so create and initialise
@ -5204,7 +5205,9 @@ static void do_ssh2_transport(void *vctx)
bombout(("expected new-keys packet from server"));
crStopV;
}
ssh->incoming_data_size = 0; /* start counting from here */
/* Start counting down the incoming-data limit for these cipher keys. */
ssh->stats.in.running = TRUE;
ssh->stats.in.remaining = ssh->max_data_size;
/*
* We've seen server NEWKEYS, so create and initialise
@ -5363,8 +5366,9 @@ static void do_ssh2_transport(void *vctx)
ssh->rekey_reason);
/* Reset the counters, so that at least this message doesn't
* hit the event log _too_ often. */
ssh->outgoing_data_size = 0;
ssh->incoming_data_size = 0;
ssh->stats.in.running = ssh->stats.out.running = TRUE;
ssh->stats.in.remaining = ssh->stats.out.remaining =
ssh->max_data_size;
(void) ssh2_timer_update(ssh, 0);
goto wait_for_rekey; /* this is still utterly horrid */
} else {
@ -8649,7 +8653,7 @@ static void ssh2_protocol_setup(Ssh ssh)
{
int i;
ssh->bpp = ssh2_bpp_new();
ssh->bpp = ssh2_bpp_new(&ssh->stats);
#ifndef NO_GSSAPI
/* Load and pick the highest GSS library on the preference list. */
@ -9055,10 +9059,8 @@ static void ssh2_timer(void *ctx, unsigned long now)
static void ssh2_general_packet_processing(Ssh ssh, PktIn *pktin)
{
ssh->incoming_data_size += pktin->encrypted_len;
if (!ssh->kex_in_progress &&
ssh->max_data_size != 0 &&
ssh->incoming_data_size > ssh->max_data_size) {
if (!ssh->kex_in_progress && ssh->max_data_size != 0 &&
ssh->state != SSH_STATE_PREPACKET && !ssh->stats.in.running) {
ssh->rekey_reason = "too much data received";
ssh->rekey_class = RK_NORMAL;
queue_idempotent_callback(&ssh->ssh2_transport_icb);
@ -9213,7 +9215,7 @@ static const char *ssh_init(Frontend *frontend, Backend **backend_handle,
ssh->pinger = NULL;
ssh->incoming_data_size = ssh->outgoing_data_size = 0L;
memset(&ssh->stats, 0, sizeof(ssh->stats));
ssh->max_data_size = parse_blocksize(conf_get_str(ssh->conf,
CONF_ssh_rekey_data));
ssh->kex_in_progress = FALSE;
@ -9371,9 +9373,22 @@ static void ssh_reconfig(Backend *be, Conf *conf)
CONF_ssh_rekey_data));
if (old_max_data_size != ssh->max_data_size &&
ssh->max_data_size != 0) {
if (ssh->outgoing_data_size > ssh->max_data_size ||
ssh->incoming_data_size > ssh->max_data_size)
rekeying = "data limit lowered";
if (ssh->max_data_size < old_max_data_size) {
unsigned long diff = old_max_data_size - ssh->max_data_size;
/* Intentionally use bitwise OR instead of logical, so
* that we decrement both counters even if the first one
* runs out */
if ((DTS_CONSUME(&ssh->stats, out, diff) != 0) |
(DTS_CONSUME(&ssh->stats, in, diff) != 0))
rekeying = "data limit lowered";
} else {
unsigned long diff = ssh->max_data_size - old_max_data_size;
if (ssh->stats.out.running)
ssh->stats.out.remaining += diff;
if (ssh->stats.in.running)
ssh->stats.in.remaining += diff;
}
}
if (conf_get_int(ssh->conf, CONF_compression) !=