Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP4:GA
openssh
openssh-cve-2023-48795.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File openssh-cve-2023-48795.patch of Package openssh
Index: openssh-7.2p2/PROTOCOL =================================================================== --- openssh-7.2p2.orig/PROTOCOL +++ openssh-7.2p2/PROTOCOL @@ -102,6 +102,25 @@ OpenSSH supports the use of ECDH in Curv described at: http://git.libssh.org/users/aris/libssh.git/plain/doc/curve25519-sha256@libssh.org.txt?h=curve25519 +1.9 transport: strict key exchange extension + +OpenSSH supports a number of transport-layer hardening measures under +a "strict KEX" feature. This feature is signalled similarly to the +RFC8305 ext-info feature: by including a additional algorithm in the +SSH2_MSG_KEXINIT kex_algorithms field. The client may append +"kex-strict-c-v00@openssh.com" to its kex_algorithms and the server +may append "kex-strict-s-v00@openssh.com". + +When endpoint that supports this extension observes this algorithm +name in a peer's KEXINIT packet, it MUST make the following changes to +the the protocol: + +a) During initial KEX, terminate the connection if any unexpected or + out-of-sequence packet is received. This includes terminating the + connection if the first packet received is not SSH2_MSG_KEXINIT. +b) At each SSH2_MSG_NEWKEYS message, reset the packet sequence number + to zero. + 2. Connection protocol changes 2.1. connection: Channel write close extension "eow@openssh.com" Index: openssh-7.2p2/kex.c =================================================================== --- openssh-7.2p2.orig/kex.c +++ openssh-7.2p2/kex.c @@ -70,7 +70,7 @@ extern const EVP_MD *evp_ssh_sha256(void #endif /* prototype */ -static int kex_choose_conf(struct ssh *); +static int kex_choose_conf(struct ssh *, uint32_t seq); static int kex_input_newkeys(int, u_int32_t, void *); static const char *proposal_names[PROPOSAL_MAX] = { @@ -230,6 +230,24 @@ kex_names_valid(const char *names) return 1; } +/* returns non-zero if proposal contains any algorithm from algs */ +static int +has_any_alg(const char *proposal, const char *algs) +{ + char *cp; + + if ((cp = match_list(proposal, algs, NULL)) == NULL) + return 0; + free(cp); + return 1; +} + +static int +kexalgs_contains (char **peer, const char *ext) +{ + return has_any_alg(peer[PROPOSAL_KEX_ALGS], ext); +} + /* * Concatenate algorithm names, avoiding duplicates in the process. * Caller must free returned string. @@ -254,7 +272,7 @@ kex_names_cat(const char *a, const char } strlcpy(ret, a, len); for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { - if (match_list(ret, p, NULL) != NULL) + if (has_any_alg(ret, p)) continue; /* Algorithm already present */ if (strlcat(ret, ",", len) >= len || strlcat(ret, p, len) >= len) { @@ -380,7 +398,12 @@ kex_protocol_error(int type, u_int32_t s struct ssh *ssh = active_state; /* XXX */ int r; - error("kex protocol error: type %d seq %u", type, seq); + /* 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: " + "unexpected packet type %u (seqnr %u)", type, seq); + } + error("type %u seq %u", type, seq); if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 || (r = sshpkt_put_u32(ssh, seq)) != 0 || (r = sshpkt_send(ssh)) != 0) @@ -441,6 +464,11 @@ kex_input_ext_info(int type, u_int32_t s ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error); if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0) return r; + if (ninfo >= 1024) { + error("SSH2_MSG_EXT_INFO with too many entries, expected " + "<=1024, received %u", ninfo); + return dispatch_protocol_error(type, seq, ssh); + } for (i = 0; i < ninfo; i++) { if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0) return r; @@ -481,6 +509,7 @@ kex_input_newkeys(int type, u_int32_t se if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0) return r; kex->done = 1; + kex->flags &= ~KEX_INITIAL; sshbuf_reset(kex->peer); /* sshbuf_reset(kex->my); */ kex->flags &= ~KEX_INIT_SENT; @@ -533,7 +562,7 @@ kex_input_kexinit(int type, u_int32_t se if (kex == NULL) return SSH_ERR_INVALID_ARGUMENT; - ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL); + ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_protocol_error); ptr = sshpkt_ptr(ssh, &dlen); if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0) return r; @@ -563,7 +592,7 @@ kex_input_kexinit(int type, u_int32_t se if (!(kex->flags & KEX_INIT_SENT)) if ((r = kex_send_kexinit(ssh)) != 0) return r; - if ((r = kex_choose_conf(ssh)) != 0) + if ((r = kex_choose_conf(ssh, seq)) != 0) return r; if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) @@ -589,6 +618,7 @@ kex_new(struct ssh *ssh, char *proposal[ if ((r = kex_prop2buf(kex->my, proposal)) != 0) goto out; kex->done = 0; + kex->flags = KEX_INITIAL; kex_reset_dispatch(ssh); r = 0; *kexp = kex; @@ -819,7 +849,7 @@ proposals_match(char *my[PROPOSAL_MAX], } static int -kex_choose_conf(struct ssh *ssh) +kex_choose_conf(struct ssh *ssh, uint32_t seq) { struct kex *kex = ssh->kex; struct newkeys *newkeys; @@ -844,16 +874,24 @@ kex_choose_conf(struct ssh *ssh) sprop=peer; } - /* Check whether client supports ext_info_c */ - if (kex->server) { - char *ext; - - ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL); - if (ext) { - kex->ext_info_c = 1; - free(ext); + /* Check whether peer supports ext_info/kex_strict */ + if ((kex->flags & KEX_INITIAL) != 0) { + if (kex->server) { + kex->ext_info_c = kexalgs_contains(peer, "ext-info-c"); + kex->kex_strict = kexalgs_contains(peer, + "kex-strict-c-v00@openssh.com"); + } else { + kex->kex_strict = kexalgs_contains(peer, + "kex-strict-s-v00@openssh.com"); } - } + if (kex->kex_strict) { + debug3("will use strict KEX ordering"); + if (seq != 0) + ssh_packet_disconnect(ssh, + "strict KEX violation: " + "KEXINIT was not the first packet"); + } + } /* Algorithm Negotiation */ if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], Index: openssh-7.2p2/kex.h =================================================================== --- openssh-7.2p2.orig/kex.h +++ openssh-7.2p2/kex.h @@ -99,6 +99,7 @@ enum kex_exchange { }; #define KEX_INIT_SENT 0x0001 +#define KEX_INITIAL 0x0002 struct sshenc { char *name; @@ -137,6 +138,7 @@ struct kex { u_int kex_type; int rsa_sha2; int ext_info_c; + int kex_strict; struct sshbuf *my; struct sshbuf *peer; sig_atomic_t done; Index: openssh-7.2p2/packet.c =================================================================== --- openssh-7.2p2.orig/packet.c +++ openssh-7.2p2/packet.c @@ -1213,6 +1213,11 @@ ssh_packet_send2_wrapped(struct ssh *ssh state->p_send.bytes += len; sshbuf_reset(state->outgoing_packet); + if (type == SSH2_MSG_NEWKEYS && ssh && ssh->kex && ssh->kex->kex_strict) { + debug("resetting send seqnr %u", state->p_send.seqnr); + state->p_send.seqnr = 0; + } + if (type == SSH2_MSG_NEWKEYS) r = ssh_set_newkeys(ssh, MODE_OUT); else if (type == SSH2_MSG_USERAUTH_SUCCESS && state->server_side) @@ -1346,8 +1351,7 @@ ssh_packet_read_seqnr(struct ssh *ssh, u /* Stay in the loop until we have received a complete packet. */ for (;;) { /* Try to read a packet from the buffer. */ - r = ssh_packet_read_poll_seqnr(ssh, typep, seqnr_p); - if (r != 0) + if ((r = ssh_packet_read_poll_seqnr(ssh, typep, seqnr_p)) != 0) break; if (!compat20 && ( *typep == SSH_SMSG_SUCCESS @@ -1743,6 +1747,7 @@ ssh_packet_read_poll2(struct ssh *ssh, u if ((r = sshbuf_consume(state->input, mac->mac_len)) != 0) goto out; } + if (seqnr_p != NULL) *seqnr_p = state->p_read.seqnr; if (++state->p_read.seqnr == 0) @@ -1808,6 +1813,10 @@ ssh_packet_read_poll2(struct ssh *ssh, u #endif /* reset for next packet */ state->packlen = 0; + if (*typep == SSH2_MSG_NEWKEYS && ssh && ssh->kex && ssh->kex->kex_strict) { + debug("resetting read seqnr %u", state->p_read.seqnr); + state->p_read.seqnr = 0; + } /* do we need to rekey? */ if (ssh_packet_need_rekeying(ssh, 0)) { @@ -1833,10 +1842,40 @@ ssh_packet_read_poll_seqnr(struct ssh *s r = ssh_packet_read_poll2(ssh, typep, seqnr_p); if (r != 0) return r; - if (*typep) { - state->keep_alive_timeouts = 0; - DBG(debug("received packet type %d", *typep)); + if (*typep == 0) { + /* no message ready */ + return 0; + } + state->keep_alive_timeouts = 0; + DBG(debug("received packet type %d", *typep)); + + /* Always process disconnect messages */ + if (*typep == SSH2_MSG_DISCONNECT) { + if ((r = sshpkt_get_u32(ssh, &reason)) != 0 || + (r = sshpkt_get_string(ssh, &msg, NULL)) != 0) + return r; + /* Ignore normal client exit notifications */ + do_log2(ssh->state->server_side && + reason == SSH2_DISCONNECT_BY_APPLICATION ? + SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_ERROR, + "Received disconnect from %s port %d:" + "%u: %.400s", ssh_remote_ipaddr(ssh), + ssh_remote_port(ssh), reason, msg); + free(msg); + return SSH_ERR_DISCONNECTED; } + + /* + * Do not implicitly handle any messages here during initial + * KEX when in strict mode. They will be need to be allowed + * explicitly by the KEX dispatch table or they will generate + * protocol errors. + */ + if (ssh->kex != NULL && + (ssh->kex->flags & KEX_INITIAL) && ssh->kex->kex_strict) + return 0; + + /* Implicitly handle transport-level messages */ switch (*typep) { case SSH2_MSG_IGNORE: debug3("Received SSH2_MSG_IGNORE"); @@ -1851,19 +1890,6 @@ ssh_packet_read_poll_seqnr(struct ssh *s debug("Remote: %.900s", msg); free(msg); break; - case SSH2_MSG_DISCONNECT: - if ((r = sshpkt_get_u32(ssh, &reason)) != 0 || - (r = sshpkt_get_string(ssh, &msg, NULL)) != 0) - return r; - /* Ignore normal client exit notifications */ - do_log2(ssh->state->server_side && - reason == SSH2_DISCONNECT_BY_APPLICATION ? - SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_ERROR, - "Received disconnect from %s port %d:" - "%u: %.400s", ssh_remote_ipaddr(ssh), - ssh_remote_port(ssh), reason, msg); - free(msg); - return SSH_ERR_DISCONNECTED; case SSH2_MSG_UNIMPLEMENTED: if ((r = sshpkt_get_u32(ssh, &seqnr)) != 0) return r; @@ -2436,6 +2462,7 @@ kex_to_blob(struct sshbuf *m, struct kex (r = sshbuf_put_u32(m, kex->we_need)) != 0 || (r = sshbuf_put_u32(m, kex->hostkey_type)) != 0 || (r = sshbuf_put_u32(m, kex->kex_type)) != 0 || + (r = sshbuf_put_u32(m, kex->kex_strict)) != 0 || (r = sshbuf_put_stringb(m, kex->my)) != 0 || (r = sshbuf_put_stringb(m, kex->peer)) != 0 || (r = sshbuf_put_u32(m, kex->flags)) != 0 || @@ -2636,6 +2663,7 @@ kex_from_blob(struct sshbuf *m, struct k (r = sshbuf_get_u32(m, &kex->we_need)) != 0 || (r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_type)) != 0 || (r = sshbuf_get_u32(m, &kex->kex_type)) != 0 || + (r = sshbuf_get_u32(m, &kex->kex_strict)) != 0 || (r = sshbuf_get_stringb(m, kex->my)) != 0 || (r = sshbuf_get_stringb(m, kex->peer)) != 0 || (r = sshbuf_get_u32(m, &kex->flags)) != 0 || @@ -2939,6 +2967,8 @@ sshpkt_disconnect(struct ssh *ssh, const vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); + debug2("sending SSH2_MSG_DISCONNECT: %s", buf); + if (compat20) { if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 || (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_PROTOCOL_ERROR)) != 0 || Index: openssh-7.2p2/sshconnect2.c =================================================================== --- openssh-7.2p2.orig/sshconnect2.c +++ openssh-7.2p2/sshconnect2.c @@ -203,8 +203,11 @@ ssh_kex2(char *host, struct sockaddr *ho xxx_host = host; xxx_hostaddr = hostaddr; - if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL) + /* Append EXT_INFO signalling to KexAlgorithms */ + if ((s = kex_names_cat(options.kex_algorithms, + "ext-info-c,kex-strict-c-v00@openssh.com")) == NULL) fatal("%s: kex_names_cat", __func__); + myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(s); #ifdef GSSAPI @@ -500,6 +503,8 @@ ssh_userauth2(const char *local_user, co ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_ACCEPT, &input_userauth_service_accept); ssh_dispatch_run(ssh, DISPATCH_BLOCK, &authctxt.success, &authctxt); /* loop until success */ + ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, dispatch_protocol_error); + pubkey_cleanup(&authctxt); ssh_dispatch_range(ssh, SSH2_MSG_USERAUTH_MIN, SSH2_MSG_USERAUTH_MAX, NULL); Index: openssh-7.2p2/sshd.c =================================================================== --- openssh-7.2p2.orig/sshd.c +++ openssh-7.2p2/sshd.c @@ -2852,9 +2852,19 @@ do_ssh2_kex(void) char *myproposal[PROPOSAL_MAX] = { KEX_SERVER }; struct kex *kex; int r; + char *s; - myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal( - options.kex_algorithms); + /* Append EXT_INFO signalling to KexAlgorithms */ + if (options.kex_algorithms == NULL) + s = strdup("kex-strict-s-v00@openssh.com"); + else + s = kex_names_cat(options.kex_algorithms, + "kex-strict-s-v00@openssh.com"); + + if (s == NULL) + fatal("%s: kex_names_cat", __func__); + + myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(s); myproposal[PROPOSAL_ENC_ALGS_CTOS] = compat_cipher_proposal( options.ciphers); myproposal[PROPOSAL_ENC_ALGS_STOC] = compat_cipher_proposal(
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor