diff --git a/kex.c b/kex.c index 0e87a9f6a..d9959077f 100644 --- a/kex.c +++ b/kex.c @@ -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; + } } } diff --git a/packet.c b/packet.c index d63c579de..462df2847 100644 --- a/packet.c +++ b/packet.c @@ -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 * Copyright (c) 1995 Tatu Ylonen , 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) diff --git a/packet.h b/packet.h index ecab22a2d..35a5f8a6e 100644 --- a/packet.h +++ b/packet.h @@ -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 @@ -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)))