From: djm@openbsd.org Date: Tue, 30 Dec 2025 00:22:58 +0000 (+0000) Subject: upstream: Enforce maximum packet/block limit during X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ca313fef2deed90668fe0706da8529310092d1dd;p=thirdparty%2Fopenssh-portable.git upstream: Enforce maximum packet/block limit during pre-authentication phase OpenSSH doesn't support rekeying before authentication completes to minimise pre-auth attack surface. Given LoginGraceTime, MaxAuthTries and strict KEX, it would be difficult to send enough data or packets before authentication completes to reach a point where rekeying is required, but we'd prefer it to be completely impossible. So this applies the default volume/packet rekeying limits to the pre-auth phase. If these limits are exceeded the connection will simply be closed. ok dtucker markus OpenBSD-Commit-ID: 70415098db739058006e4ebd1630b6bae8cc8bf6 --- diff --git a/packet.c b/packet.c index 2a5a56a88..2df7a97b7 100644 --- a/packet.c +++ b/packet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.c,v 1.327 2025/12/05 06:16:27 dtucker Exp $ */ +/* $OpenBSD: packet.c,v 1.328 2025/12/30 00:22:58 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -184,6 +184,7 @@ struct session_state { struct packet_state p_read, p_send; /* Volume-based rekeying */ + u_int64_t hard_max_blocks_in, hard_max_blocks_out; u_int64_t max_blocks_in, max_blocks_out, rekey_limit; /* Time-based rekeying */ @@ -979,7 +980,7 @@ ssh_set_newkeys(struct ssh *ssh, int mode) struct sshcomp *comp; struct sshcipher_ctx **ccp; struct packet_state *ps; - u_int64_t *max_blocks; + u_int64_t *max_blocks, *hard_max_blocks; const char *wmsg; int r, crypt_type; const char *dir = mode == MODE_OUT ? "out" : "in"; @@ -990,11 +991,13 @@ ssh_set_newkeys(struct ssh *ssh, int mode) ccp = &state->send_context; crypt_type = CIPHER_ENCRYPT; ps = &state->p_send; + hard_max_blocks = &state->hard_max_blocks_out; max_blocks = &state->max_blocks_out; } else { ccp = &state->receive_context; crypt_type = CIPHER_DECRYPT; ps = &state->p_read; + hard_max_blocks = &state->hard_max_blocks_in; max_blocks = &state->max_blocks_in; } if (state->newkeys[mode] != NULL) { @@ -1055,25 +1058,59 @@ ssh_set_newkeys(struct ssh *ssh, int mode) * See RFC4344 section 3.2. */ if (enc->block_size >= 16) - *max_blocks = (u_int64_t)1 << (enc->block_size*2); + *hard_max_blocks = (u_int64_t)1 << (enc->block_size*2); else - *max_blocks = ((u_int64_t)1 << 30) / enc->block_size; - if (state->rekey_limit) + *hard_max_blocks = ((u_int64_t)1 << 30) / enc->block_size; + *max_blocks = *hard_max_blocks; + if (state->rekey_limit) { *max_blocks = MINIMUM(*max_blocks, state->rekey_limit / enc->block_size); + } debug("rekey %s after %llu blocks", dir, (unsigned long long)*max_blocks); return 0; } #define MAX_PACKETS (1U<<31) +/* + * Checks whether the packet- or block- based rekeying limits have been + * exceeded. If the 'hard' flag is set, the checks are performed against the + * absolute maximum we're willing to accept for the given cipher. Otherwise + * the checks are performed against the RekeyLimit volume, which may be lower. + */ +static inline int +ssh_packet_check_rekey_blocklimit(struct ssh *ssh, u_int packet_len, int hard) +{ + struct session_state *state = ssh->state; + u_int32_t out_blocks; + const u_int64_t max_blocks_in = hard ? + state->hard_max_blocks_in : state->max_blocks_in; + const u_int64_t max_blocks_out = hard ? + state->hard_max_blocks_out : state->max_blocks_out; + + /* + * Always rekey when MAX_PACKETS sent in either direction + * As per RFC4344 section 3.1 we do this after 2^31 packets. + */ + if (state->p_send.packets > MAX_PACKETS || + state->p_read.packets > MAX_PACKETS) + return 1; + + /* Rekey after (cipher-specific) maximum blocks */ + out_blocks = ROUNDUP(packet_len, + state->newkeys[MODE_OUT]->enc.block_size); + return (max_blocks_out && + (state->p_send.blocks + out_blocks > max_blocks_out)) || + (max_blocks_in && + (state->p_read.blocks > max_blocks_in)); +} + static int ssh_packet_need_rekeying(struct ssh *ssh, u_int outbound_packet_len) { struct session_state *state = ssh->state; - u_int32_t out_blocks; - /* XXX client can't cope with rekeying pre-auth */ + /* Don't attempt rekeying during pre-auth */ if (!state->after_authentication) return 0; @@ -1097,26 +1134,30 @@ ssh_packet_need_rekeying(struct ssh *ssh, u_int outbound_packet_len) (int64_t)state->rekey_time + state->rekey_interval <= monotime()) return 1; - /* - * Always rekey when MAX_PACKETS sent in either direction - * As per RFC4344 section 3.1 we do this after 2^31 packets. - */ - if (state->p_send.packets > MAX_PACKETS || - state->p_read.packets > MAX_PACKETS) - return 1; + return ssh_packet_check_rekey_blocklimit(ssh, outbound_packet_len, 0); +} - /* Rekey after (cipher-specific) maximum blocks */ - out_blocks = ROUNDUP(outbound_packet_len, - state->newkeys[MODE_OUT]->enc.block_size); - return (state->max_blocks_out && - (state->p_send.blocks + out_blocks > state->max_blocks_out)) || - (state->max_blocks_in && - (state->p_read.blocks > state->max_blocks_in)); +/* Checks that the hard rekey limits have not been exceeded during preauth */ +static int +ssh_packet_check_rekey_preauth(struct ssh *ssh, u_int outgoing_packet_len) +{ + if (ssh->state->after_authentication) + return 0; + + if (ssh_packet_check_rekey_blocklimit(ssh, 0, 1)) { + error("RekeyLimit exceeded before authentication completed"); + return SSH_ERR_NEED_REKEY; + } + return 0; } int ssh_packet_check_rekey(struct ssh *ssh) { + int r; + + if ((r = ssh_packet_check_rekey_preauth(ssh, 0)) != 0) + return r; if (!ssh_packet_need_rekeying(ssh, 0)) return 0; debug3_f("rekex triggered"); @@ -1374,6 +1415,11 @@ ssh_packet_send2(struct ssh *ssh) need_rekey = !ssh_packet_type_is_kex(type) && ssh_packet_need_rekeying(ssh, sshbuf_len(state->outgoing_packet)); + /* Enforce hard rekey limit during pre-auth */ + if (!state->rekeying && !ssh_packet_type_is_kex(type) && + (r = ssh_packet_check_rekey_preauth(ssh, 0)) != 0) + return r; + /* * During rekeying we can only send key exchange messages. * Queue everything else.