upstream: make strict KEX errors return an error rather than

fatal()ing the process. This avoids ssh-keyscan crashing out when one host of
many fails strict KEX.

To avoid the risk of continuing past such a failure in ssh or sshd, make
such failures "poison" the packet code so that future attempts to send
or receive packets always an error.

Anthropic ANT-2026-TU0DN0EM (poisoning bit by me)

OpenBSD-Commit-ID: 4eed6b3b2960add40227a6dd9d6b4ea2b053772c
This commit is contained in:
djm@openbsd.org authored and Damien Miller committed 2026-10-30 20:14:06 +11:00
1 parent c0ea89627a
commit f7a2c31dbf
3 files changed
+51 -7

No files matched your search

+7 -5
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: kex.c,v 1.197 2026/10/30 01:16:44 djm Exp $ */
/* $OpenBSD: kex.c,v 1.198 2026/10/30 04:36:21 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
*
@@ -235,7 +235,7 @@ kex_protocol_error(int type, uint32_t seq, struct ssh *ssh)
/* If in strict mode, any unexpected message is an error */
if ((ssh->kex->flags & KEX_INITIAL) && ssh->kex->kex_strict) {
ssh_packet_disconnect(ssh, "strict KEX violation: "
return sshpkt_protocol_error(ssh, "strict KEX violation: "
"unexpected packet type %u (seqnr %u)", type, seq);
}
error_f("type %u seq %u", type, seq);
@@ -660,7 +660,7 @@ kex_input_kexinit(int type, uint32_t seq, struct ssh *ssh)
free(kex->name);
kex->name = NULL;
if ((kex->flags & KEX_INIT_RECVD) != 0) {
ssh_packet_disconnect(ssh,
return sshpkt_protocol_error(ssh,
"multiple KEXINIT received from peer");
}
kex->flags |= KEX_INIT_RECVD;
@@ -1004,10 +1004,12 @@ kex_choose_conf(struct ssh *ssh, uint32_t seq)
}
if (kex->kex_strict) {
debug3_f("will use strict KEX ordering");
if (seq != 0)
ssh_packet_disconnect(ssh,
if (seq != 0) {
r = sshpkt_protocol_error(ssh,
"strict KEX violation: "
"KEXINIT was not the first packet");
goto out;
}
}
}
+41 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: packet.c,v 1.346 2026/09/22 22:51:43 job Exp $ */
/* $OpenBSD: packet.c,v 1.347 2026/10/30 04:36:21 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -224,6 +224,9 @@ struct session_state {
/* Nagle disabled on socket */
int nodelay_set;
/* Set to permanently poison this connection instance */
int poisoned;
/* Hook for fuzzing inbound packets */
ssh_packet_hook_fn *hook_in;
void *hook_in_ctx;
@@ -1501,6 +1504,9 @@ ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, uint32_t *seqnr_p)
DBG(debug("packet_read()"));
if (state->poisoned)
return SSH_ERR_PROTOCOL_ERROR;
/*
* Since we are blocking, ensure that all written packets have
* been sent.
@@ -1592,6 +1598,8 @@ ssh_packet_read_poll2_mux(struct ssh *ssh, u_char *typep, uint32_t *seqnr_p)
if (ssh->kex)
return SSH_ERR_INTERNAL_ERROR;
if (state->poisoned)
return SSH_ERR_PROTOCOL_ERROR;
*typep = SSH_MSG_NONE;
cp = sshbuf_ptr(state->input);
if (state->packlen == 0) {
@@ -1634,6 +1642,8 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, uint32_t *seqnr_p)
if (state->mux)
return ssh_packet_read_poll2_mux(ssh, typep, seqnr_p);
if (state->poisoned)
return SSH_ERR_PROTOCOL_ERROR;
*typep = SSH_MSG_NONE;
@@ -2937,6 +2947,8 @@ sshpkt_msg_ignore(struct ssh *ssh, u_int nbytes)
int
sshpkt_send(struct ssh *ssh)
{
if (ssh->state && ssh->state->poisoned)
return SSH_ERR_PROTOCOL_ERROR;
if (ssh->state && ssh->state->mux)
return ssh_packet_send_mux(ssh);
return ssh_packet_send2(ssh);
@@ -2963,6 +2975,34 @@ sshpkt_disconnect(struct ssh *ssh, const char *fmt,...)
return 0;
}
/*
* Logs a protocol violation by the peer, sends a disconnect packet and
* returns SSH_ERR_PROTOCOL_ERROR for the caller to propagate.
* This does not terminate the connection, but does "poison" the packet
* code so further attempts to read/write will fail with the same error.
*/
int
sshpkt_protocol_error(struct ssh *ssh, const char *fmt,...)
{
char buf[1024], remote_id[512];
va_list args;
int r;
sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id));
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
logit("Disconnecting %s: %.100s", remote_id, buf);
if ((r = sshpkt_disconnect(ssh, "%s", buf)) != 0 ||
(r = ssh_packet_write_wait(ssh)) != 0)
return r;
if (ssh->state == NULL)
cleanup_exit(255); /* Can't poison? then exit */
ssh->state->poisoned = 1;
return SSH_ERR_PROTOCOL_ERROR;
}
/* roundup current message to pad bytes */
int
sshpkt_add_padding(struct ssh *ssh, u_char pad)
+3 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: packet.h,v 1.108 2026/09/22 00:24:47 dtucker Exp $ */
/* $OpenBSD: packet.h,v 1.109 2026/10/30 04:36:21 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -174,6 +174,8 @@ int sshpkt_start(struct ssh *ssh, u_char type);
int sshpkt_send(struct ssh *ssh);
int sshpkt_disconnect(struct ssh *, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
int sshpkt_protocol_error(struct ssh *, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
int sshpkt_add_padding(struct ssh *, u_char);
void sshpkt_fatal(struct ssh *ssh, int r, const char *fmt, ...)
__attribute__((format(printf, 3, 4)))