mirror of
https://github.com/openssh/openssh-portable.git
synced 2026-11-06 05:16:43 +00:00
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:
1 parent
c0ea89627a
commit
f7a2c31dbf
3 files changed
+51
-7
No files matched your search
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)))
|
||||
|
||||
Reference in new issue
Block a user