[openssh-commits] [openssh] 02/04: upstream: Enforce maximum packet/block limit during
git+noreply at mindrot.org
git+noreply at mindrot.org
Tue Dec 30 11:37:52 AEDT 2025
This is an automated email from the git hooks/post-receive script.
djm pushed a commit to branch master
in repository openssh.
commit ca313fef2deed90668fe0706da8529310092d1dd
Author: djm at openbsd.org <djm at openbsd.org>
AuthorDate: Tue Dec 30 00:22:58 2025 +0000
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
---
packet.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++----------------
1 file changed, 68 insertions(+), 22 deletions(-)
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 <ylo at cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, 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)
-static int
-ssh_packet_need_rekeying(struct ssh *ssh, u_int outbound_packet_len)
+/*
+ * 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;
- /* XXX client can't cope with rekeying pre-auth */
+ /*
+ * 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;
+
+ /* 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.
--
To stop receiving notification emails like this one, please contact
djm at mindrot.org.
More information about the openssh-commits
mailing list