!226 [sync] PR-224: fix CVE-2023-48795 and CVE-2023-51385

From: @openeuler-sync-bot 
Reviewed-by: @robertxw 
Signed-off-by: @robertxw
This commit is contained in:
openeuler-ci-bot 2023-12-25 07:15:28 +00:00 committed by Gitee
commit 892b0caa6d
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
5 changed files with 1051 additions and 1 deletions

View File

@ -0,0 +1,492 @@
From 1edb00c58f8a6875fad6a497aa2bacf37f9e6cd5 Mon Sep 17 00:00:00 2001
From: "djm@openbsd.org" <djm@openbsd.org>
Date: Mon, 18 Dec 2023 14:45:17 +0000
Subject: [PATCH] upstream: implement "strict key exchange" in ssh and sshd
This adds a protocol extension to improve the integrity of the SSH
transport protocol, particular in and around the initial key exchange
(KEX) phase.
Full details of the extension are in the PROTOCOL file.
with markus@
OpenBSD-Commit-ID: 2a66ac962f0a630d7945fee54004ed9e9c439f14
Reference:https://github.com/openssh/openssh-portable/commit/1edb00c58f8a6875fad6a497aa2bacf37f9e6cd5
Conflict:upstream: Fix signature algorithm selection logic for UpdateHostkeys on the server side
---
PROTOCOL | 28 +++++++++++++-
kex.c | 80 +++++++++++++++++++++++++++++----------
kex.h | 3 +-
packet.c | 103 +++++++++++++++++++++++++++++---------------------
packet.h | 3 +-
sshconnect2.c | 13 ++-----
6 files changed, 152 insertions(+), 78 deletions(-)
diff --git a/PROTOCOL b/PROTOCOL
index 3141cda..9f02bb9 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -102,6 +102,32 @@ OpenSSH supports the use of ECDH in Curve25519 for key exchange as
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
+RFC8308 ext-info feature: by including a additional algorithm in the
+initiial 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". These pseudo-algorithms
+are only valid in the initial SSH2_MSG_KEXINIT and MUST be ignored
+if they are present in subsequent SSH2_MSG_KEXINIT packets.
+
+When an 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.
+ Unexpected packets for the purpose of strict KEX include messages
+ that are otherwise valid at any time during the connection such as
+ SSH2_MSG_DEBUG and SSH2_MSG_IGNORE.
+b) After sending or receiving a SSH2_MSG_NEWKEYS message, reset the
+ packet sequence number to zero. This behaviour persists for the
+ duration of the connection (i.e. not just the first
+ SSH2_MSG_NEWKEYS).
+
2. Connection protocol changes
2.1. connection: Channel write close extension "eow@openssh.com"
@@ -575,4 +601,4 @@ OpenSSH's connection multiplexing uses messages as described in
PROTOCOL.mux over a Unix domain socket for communications between a
master instance and later clients.
-$OpenBSD: PROTOCOL,v 1.42 2021/08/09 23:47:44 djm Exp $
+$OpenBSD: PROTOCOL,v 1.50 2023/12/18 14:45:17 djm Exp $
diff --git a/kex.c b/kex.c
index 2afa087..5e437f9 100644
--- a/kex.c
+++ b/kex.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kex.c,v 1.178 2023/03/12 10:40:39 dtucker Exp $ */
+/* $OpenBSD: kex.c,v 1.183 2023/12/18 14:45:17 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
*
@@ -73,7 +73,7 @@
#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, struct ssh *);
static const char *proposal_names[PROPOSAL_MAX] = {
@@ -218,6 +218,18 @@ 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;
+}
+
/*
* Concatenate algorithm names, avoiding duplicates in the process.
* Caller must free returned string.
@@ -225,7 +237,7 @@ kex_names_valid(const char *names)
char *
kex_names_cat(const char *a, const char *b)
{
- char *ret = NULL, *tmp = NULL, *cp, *p, *m;
+ char *ret = NULL, *tmp = NULL, *cp, *p;
size_t len;
if (a == NULL || *a == '\0')
@@ -242,10 +254,8 @@ kex_names_cat(const char *a, const char *b)
}
strlcpy(ret, a, len);
for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) {
- if ((m = match_list(ret, p, NULL)) != NULL) {
- free(m);
+ if (has_any_alg(ret, p))
continue; /* Algorithm already present */
- }
if (strlcat(ret, ",", len) >= len ||
strlcat(ret, p, len) >= len) {
free(tmp);
@@ -375,15 +385,23 @@ kex_proposal_populate_entries(struct ssh *ssh, char *prop[PROPOSAL_MAX],
const char *defpropclient[PROPOSAL_MAX] = { KEX_CLIENT };
const char **defprop = ssh->kex->server ? defpropserver : defpropclient;
u_int i;
+ char *cp;
if (prop == NULL)
fatal_f("proposal missing");
+ /* Append EXT_INFO signalling to KexAlgorithms */
+ if (kexalgos == NULL)
+ kexalgos = defprop[PROPOSAL_KEX_ALGS];
+ if ((cp = kex_names_cat(kexalgos, ssh->kex->server ?
+ "kex-strict-s-v00@openssh.com" :
+ "ext-info-c,kex-strict-c-v00@openssh.com")) == NULL)
+ fatal_f("kex_names_cat");
+
for (i = 0; i < PROPOSAL_MAX; i++) {
switch(i) {
case PROPOSAL_KEX_ALGS:
- prop[i] = compat_kex_proposal(ssh,
- kexalgos ? kexalgos : defprop[i]);
+ prop[i] = compat_kex_proposal(ssh, cp);
break;
case PROPOSAL_ENC_ALGS_CTOS:
case PROPOSAL_ENC_ALGS_STOC:
@@ -404,6 +422,7 @@ kex_proposal_populate_entries(struct ssh *ssh, char *prop[PROPOSAL_MAX],
prop[i] = xstrdup(defprop[i]);
}
}
+ free(cp);
}
void
@@ -531,7 +550,12 @@ kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh)
{
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_f("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)
@@ -606,7 +630,7 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
if (ninfo >= 1024) {
error("SSH2_MSG_EXT_INFO with too many entries, expected "
"<=1024, received %u", ninfo);
- return SSH_ERR_INVALID_FORMAT;
+ return dispatch_protocol_error(type, seq, ssh);
}
for (i = 0; i < ninfo; i++) {
if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0)
@@ -708,7 +732,7 @@ kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)
error_f("no kex");
return SSH_ERR_INTERNAL_ERROR;
}
- 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;
@@ -744,7 +768,7 @@ kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)
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)
@@ -1022,7 +1046,13 @@ proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
}
static int
-kex_choose_conf(struct ssh *ssh)
+kexalgs_contains(char **peer, const char *ext)
+{
+ return has_any_alg(peer[PROPOSAL_KEX_ALGS], ext);
+}
+
+static int
+kex_choose_conf(struct ssh *ssh, uint32_t seq)
{
struct kex *kex = ssh->kex;
struct newkeys *newkeys;
@@ -1047,13 +1077,23 @@ kex_choose_conf(struct ssh *ssh)
sprop=peer;
}
- /* Check whether client supports ext_info_c */
- if (kex->server && (kex->flags & KEX_INITIAL)) {
- char *ext;
-
- ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL);
- kex->ext_info_c = (ext != NULL);
- 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_f("will use strict KEX ordering");
+ if (seq != 0)
+ ssh_packet_disconnect(ssh,
+ "strict KEX violation: "
+ "KEXINIT was not the first packet");
+ }
}
/* Algorithm Negotiation */
diff --git a/kex.h b/kex.h
index 87ba7c8..1374a8c 100644
--- a/kex.h
+++ b/kex.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: kex.h,v 1.118 2023/03/06 12:14:48 dtucker Exp $ */
+/* $OpenBSD: kex.h,v 1.120 2023/12/18 14:45:17 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@@ -153,6 +153,7 @@ struct kex {
u_int kex_type;
char *server_sig_algs;
int ext_info_c;
+ int kex_strict;
struct sshbuf *my;
struct sshbuf *peer;
struct sshbuf *client_version;
diff --git a/packet.c b/packet.c
index 96ff2ac..95c1dd1 100644
--- a/packet.c
+++ b/packet.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.c,v 1.301 2021/07/16 09:00:23 djm Exp $ */
+/* $OpenBSD: packet.c,v 1.313 2023/12/18 14:45:17 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1221,8 +1221,13 @@ ssh_packet_send2_wrapped(struct ssh *ssh)
sshbuf_dump(state->output, stderr);
#endif
/* increment sequence number for outgoing packets */
- if (++state->p_send.seqnr == 0)
+ if (++state->p_send.seqnr == 0) {
+ if ((ssh->kex->flags & KEX_INITIAL) != 0) {
+ ssh_packet_disconnect(ssh, "outgoing sequence number "
+ "wrapped during initial key exchange");
+ }
logit("outgoing seqnr wraps around");
+ }
if (++state->p_send.packets == 0)
if (!(ssh->compat & SSH_BUG_NOREKEY))
return SSH_ERR_NEED_REKEY;
@@ -1230,6 +1235,11 @@ ssh_packet_send2_wrapped(struct ssh *ssh)
state->p_send.bytes += len;
sshbuf_reset(state->outgoing_packet);
+ if (type == SSH2_MSG_NEWKEYS && ssh->kex->kex_strict) {
+ debug_f("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)
@@ -1362,8 +1372,7 @@ ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
/* 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 we got a packet, return it. */
if (*typep != SSH_MSG_NONE)
@@ -1650,10 +1636,16 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
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)
+ if (++state->p_read.seqnr == 0) {
+ if ((ssh->kex->flags & KEX_INITIAL) != 0) {
+ ssh_packet_disconnect(ssh, "incoming sequence number "
+ "wrapped during initial key exchange");
+ }
logit("incoming seqnr wraps around");
+ }
if (++state->p_read.packets == 0)
if (!(ssh->compat & SSH_BUG_NOREKEY))
return SSH_ERR_NEED_REKEY;
@@ -1719,6 +1711,10 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
#endif
/* reset for next packet */
state->packlen = 0;
+ if (*typep == SSH2_MSG_NEWKEYS && ssh->kex->kex_strict) {
+ debug_f("resetting read seqnr %u", state->p_read.seqnr);
+ state->p_read.seqnr = 0;
+ }
if ((r = ssh_packet_check_rekey(ssh)) != 0)
return r;
@@ -1739,10 +1735,39 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
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");
@@ -1757,19 +1782,6 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
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;
@@ -2288,6 +2300,7 @@ kex_to_blob(struct sshbuf *m, struct kex *kex)
(r = sshbuf_put_u32(m, kex->hostkey_type)) != 0 ||
(r = sshbuf_put_u32(m, kex->hostkey_nid)) != 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_stringb(m, kex->client_version)) != 0 ||
@@ -2450,6 +2463,7 @@ kex_from_blob(struct sshbuf *m, struct kex **kexp)
(r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_type)) != 0 ||
(r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_nid)) != 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_stringb(m, kex->client_version)) != 0 ||
@@ -2778,6 +2792,7 @@ sshpkt_disconnect(struct ssh *ssh, const char *fmt,...)
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
+ debug2_f("sending SSH2_MSG_DISCONNECT: %s", buf);
if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 ||
(r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_PROTOCOL_ERROR)) != 0 ||
(r = sshpkt_put_cstring(ssh, buf)) != 0 ||
diff --git a/packet.h b/packet.h
index b3d4280..047c111 100644
--- a/packet.h
+++ b/packet.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.h,v 1.93 2021/07/16 09:00:23 djm Exp $ */
+/* $OpenBSD: packet.h,v 1.96 2023/12/18 14:45:17 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
diff --git a/sshconnect2.c b/sshconnect2.c
index 9267534..701e02d 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect2.c,v 1.364 2023/03/06 12:14:48 dtucker Exp $ */
+/* $OpenBSD: sshconnect2.c,v 1.370 2023/12/18 14:45:17 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Damien Miller. All rights reserved.
@@ -427,7 +427,6 @@ struct cauthmethod {
};
static int input_userauth_service_accept(int, u_int32_t, struct ssh *);
-static int input_userauth_ext_info(int, u_int32_t, struct ssh *);
static int input_userauth_success(int, u_int32_t, struct ssh *);
static int input_userauth_failure(int, u_int32_t, struct ssh *);
static int input_userauth_banner(int, u_int32_t, struct ssh *);
@@ -548,7 +547,7 @@ ssh_userauth2(struct ssh *ssh, const char *local_user,
ssh->authctxt = &authctxt;
ssh_dispatch_init(ssh, &input_userauth_error);
- ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_ext_info);
+ ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, kex_input_ext_info);
ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_ACCEPT, &input_userauth_service_accept);
ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt.success); /* loop until success */
pubkey_cleanup(ssh);
@@ -604,13 +603,6 @@ input_userauth_service_accept(int type, u_int32_t seq, struct ssh *ssh)
return r;
}
-/* ARGSUSED */
-static int
-input_userauth_ext_info(int type, u_int32_t seqnr, struct ssh *ssh)
-{
- return kex_input_ext_info(type, seqnr, ssh);
-}
-
void
userauth(struct ssh *ssh, char *authlist)
{
@@ -692,6 +684,7 @@ input_userauth_success(int type, u_int32_t seq, struct ssh *ssh)
free(authctxt->methoddata);
authctxt->methoddata = NULL;
authctxt->success = 1; /* break out */
+ ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, dispatch_protocol_error);
return 0;
}
--
2.33.0

View File

@ -0,0 +1,101 @@
From 7ef3787c84b6b524501211b11a26c742f829af1a Mon Sep 17 00:00:00 2001
From: "djm@openbsd.org" <djm@openbsd.org>
Date: Mon, 18 Dec 2023 14:47:44 +0000
Subject: [PATCH] upstream: ban user/hostnames with most shell metacharacters
This makes ssh(1) refuse user or host names provided on the
commandline that contain most shell metacharacters.
Some programs that invoke ssh(1) using untrusted data do not filter
metacharacters in arguments they supply. This could create
interactions with user-specified ProxyCommand and other directives
that allow shell injection attacks to occur.
It's a mistake to invoke ssh(1) with arbitrary untrusted arguments,
but getting this stuff right can be tricky, so this should prevent
most obvious ways of creating risky situations. It however is not
and cannot be perfect: ssh(1) has no practical way of interpreting
what shell quoting rules are in use and how they interact with the
user's specified ProxyCommand.
To allow configurations that use strange user or hostnames to
continue to work, this strictness is applied only to names coming
from the commandline. Names specified using User or Hostname
directives in ssh_config(5) are not affected.
feedback/ok millert@ markus@ dtucker@ deraadt@
OpenBSD-Commit-ID: 3b487348b5964f3e77b6b4d3da4c3b439e94b2d9
Conflict:NA
Reference:https://anongit.mindrot.org/openssh.git/commit?id=7ef3787c84b6b524501211b11a26c742f829af1a
---
ssh.c | 41 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 40 insertions(+), 1 deletion(-)
diff --git a/ssh.c b/ssh.c
index 35c48e62d..48d93ddf2 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh.c,v 1.575 2022/07/01 00:36:30 djm Exp $ */
+/* $OpenBSD: ssh.c,v 1.599 2023/12/18 14:47:44 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -626,6 +626,41 @@ ssh_conn_info_free(struct ssh_conn_info *cinfo)
free(cinfo);
}
+static int
+valid_hostname(const char *s)
+{
+ size_t i;
+
+ if (*s == '-')
+ return 0;
+ for (i = 0; s[i] != 0; i++) {
+ if (strchr("'`\"$\\;&<>|(){}", s[i]) != NULL ||
+ isspace((u_char)s[i]) || iscntrl((u_char)s[i]))
+ return 0;
+ }
+ return 1;
+}
+
+static int
+valid_ruser(const char *s)
+{
+ size_t i;
+
+ if (*s == '-')
+ return 0;
+ for (i = 0; s[i] != 0; i++) {
+ if (strchr("'`\";&<>|(){}", s[i]) != NULL)
+ return 0;
+ /* Disallow '-' after whitespace */
+ if (isspace((u_char)s[i]) && s[i + 1] == '-')
+ return 0;
+ /* Disallow \ in last position */
+ if (s[i] == '\\' && s[i + 1] == '\0')
+ return 0;
+ }
+ return 1;
+}
+
/*
* Main program for the ssh client.
*/
@@ -1118,6 +1153,10 @@ main(int ac, char **av)
if (!host)
usage();
+ if (!valid_hostname(host))
+ fatal("hostname contains invalid characters");
+ if (options.user != NULL && !valid_ruser(options.user))
+ fatal("remote username contains invalid characters");
host_arg = xstrdup(host);
/* Initialize the command to execute on remote host. */
--
2.23.0

View File

@ -0,0 +1,41 @@
From d95af508e78c0cd3dce56b83853baaa59ae295cf Mon Sep 17 00:00:00 2001
From: "dtucker@openbsd.org" <dtucker@openbsd.org>
Date: Sun, 12 Mar 2023 10:40:39 +0000
Subject: [PATCH] upstream: Limit number of entries in SSH2_MSG_EXT_INFO
request. This is already constrained by the maximum SSH packet size but this
makes it explicit. Prompted by Coverity CID 291868, ok djm@ markus@
OpenBSD-Commit-ID: aea023819aa44a2dcb9dd0fbec10561896fc3a09
Conflict:NA
Reference:https://github.com/openssh/openssh-portable/commit/d95af508e78c0cd3dce56b83853baaa59ae295cf
---
kex.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/kex.c b/kex.c
index b681c58..2afa087 100644
--- a/kex.c
+++ b/kex.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kex.c,v 1.176 2023/03/06 12:14:48 dtucker Exp $ */
+/* $OpenBSD: kex.c,v 1.178 2023/03/12 10:40:39 dtucker Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
*
@@ -603,6 +603,11 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
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 SSH_ERR_INVALID_FORMAT;
+ }
for (i = 0; i < ninfo; i++) {
if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0)
return r;
--
2.33.0

View File

@ -0,0 +1,402 @@
From 9641753e0fd146204d57b2a4165f552a81afade4 Mon Sep 17 00:00:00 2001
From: "dtucker@openbsd.org" <dtucker@openbsd.org>
Date: Mon, 6 Mar 2023 12:14:48 +0000
Subject: [PATCH] upstream: Refactor creation of KEX proposal.
This adds kex_proposal_populate_entries (and corresponding free) which
populates the KEX proposal array with dynamically allocated strings.
This replaces the previous mix of static and dynamic that has been the
source of previous leaks and bugs. Remove unused compat functions.
With & ok djm@.
OpenBSD-Commit-ID: f2f99da4aae2233cb18bf9c749320c5e040a9c7b
Reference:https://github.com/openssh/openssh-portable/commit/9641753e0fd146204d57b2a4165f552a81afade4
Conflict:Remove now-unused compat bit SSH_BUG_RSASIGMD5
gssapi-keyex
---
compat.c | 35 ++------------------------
compat.h | 6 ++---
kex.c | 58 ++++++++++++++++++++++++++++++++++++++++++-
kex.h | 5 +++-
sshconnect2.c | 68 +++++++++++++++++++--------------------------------
sshd.c | 37 +++++++++-------------------
6 files changed, 102 insertions(+), 107 deletions(-)
diff --git a/compat.c b/compat.c
index 555a372..4f8a1fb 100644
--- a/compat.c
+++ b/compat.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: compat.c,v 1.121 2023/02/02 12:10:05 djm Exp $ */
+/* $OpenBSD: compat.c,v 1.126 2023/03/06 12:14:48 dtucker Exp $ */
/*
* Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved.
*
@@ -36,7 +36,6 @@
#include "compat.h"
#include "log.h"
#include "match.h"
-#include "kex.h"
/* determine bug flags from SSH protocol banner */
void
@@ -158,37 +157,7 @@ compat_banner(struct ssh *ssh, const char *version)
/* Always returns pointer to allocated memory, caller must free. */
char *
-compat_cipher_proposal(struct ssh *ssh, char *cipher_prop)
-{
- if (!(ssh->compat & SSH_BUG_BIGENDIANAES))
- return xstrdup(cipher_prop);
- debug2_f("original cipher proposal: %s", cipher_prop);
- if ((cipher_prop = match_filter_denylist(cipher_prop, "aes*")) == NULL)
- fatal("match_filter_denylist failed");
- debug2_f("compat cipher proposal: %s", cipher_prop);
- if (*cipher_prop == '\0')
- fatal("No supported ciphers found");
- return cipher_prop;
-}
-
-/* Always returns pointer to allocated memory, caller must free. */
-char *
-compat_pkalg_proposal(struct ssh *ssh, char *pkalg_prop)
-{
- if (!(ssh->compat & SSH_BUG_RSASIGMD5))
- return xstrdup(pkalg_prop);
- debug2_f("original public key proposal: %s", pkalg_prop);
- if ((pkalg_prop = match_filter_denylist(pkalg_prop, "ssh-rsa")) == NULL)
- fatal("match_filter_denylist failed");
- debug2_f("compat public key proposal: %s", pkalg_prop);
- if (*pkalg_prop == '\0')
- fatal("No supported PK algorithms found");
- return pkalg_prop;
-}
-
-/* Always returns pointer to allocated memory, caller must free. */
-char *
-compat_kex_proposal(struct ssh *ssh, char *p)
+compat_kex_proposal(struct ssh *ssh, const char *p)
{
char *cp = NULL, *cp2 = NULL;
diff --git a/compat.h b/compat.h
index 167409b..013c855 100644
--- a/compat.h
+++ b/compat.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: compat.h,v 1.57 2021/06/06 03:40:39 djm Exp $ */
+/* $OpenBSD: compat.h,v 1.62 2023/03/06 12:14:48 dtucker Exp $ */
/*
* Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved.
@@ -61,7 +61,5 @@
struct ssh;
void compat_banner(struct ssh *, const char *);
-char *compat_cipher_proposal(struct ssh *, char *);
-char *compat_pkalg_proposal(struct ssh *, char *);
-char *compat_kex_proposal(struct ssh *, char *);
+char *compat_kex_proposal(struct ssh *, const char *);
#endif
diff --git a/kex.c b/kex.c
index e8c2741..b681c58 100644
--- a/kex.c
+++ b/kex.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kex.c,v 1.168 2021/04/03 06:18:40 djm Exp $ */
+/* $OpenBSD: kex.c,v 1.176 2023/03/06 12:14:48 dtucker Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
*
@@ -60,6 +60,7 @@
#include "misc.h"
#include "dispatch.h"
#include "monitor.h"
+#include "myproposal.h"
#include "xmalloc.h"
#include "ssherr.h"
@@ -359,6 +360,61 @@ kex_assemble_names(char **listp, const char *def, const char *all)
return r;
}
+/*
+ * Fill out a proposal array with dynamically allocated values, which may
+ * be modified as required for compatibility reasons.
+ * Any of the options may be NULL, in which case the default is used.
+ * Array contents must be freed by calling kex_proposal_free_entries.
+ */
+void
+kex_proposal_populate_entries(struct ssh *ssh, char *prop[PROPOSAL_MAX],
+ const char *kexalgos, const char *ciphers, const char *macs,
+ const char *comp, const char *hkalgs)
+{
+ const char *defpropserver[PROPOSAL_MAX] = { KEX_SERVER };
+ const char *defpropclient[PROPOSAL_MAX] = { KEX_CLIENT };
+ const char **defprop = ssh->kex->server ? defpropserver : defpropclient;
+ u_int i;
+
+ if (prop == NULL)
+ fatal_f("proposal missing");
+
+ for (i = 0; i < PROPOSAL_MAX; i++) {
+ switch(i) {
+ case PROPOSAL_KEX_ALGS:
+ prop[i] = compat_kex_proposal(ssh,
+ kexalgos ? kexalgos : defprop[i]);
+ break;
+ case PROPOSAL_ENC_ALGS_CTOS:
+ case PROPOSAL_ENC_ALGS_STOC:
+ prop[i] = xstrdup(ciphers ? ciphers : defprop[i]);
+ break;
+ case PROPOSAL_MAC_ALGS_CTOS:
+ case PROPOSAL_MAC_ALGS_STOC:
+ prop[i] = xstrdup(macs ? macs : defprop[i]);
+ break;
+ case PROPOSAL_COMP_ALGS_CTOS:
+ case PROPOSAL_COMP_ALGS_STOC:
+ prop[i] = xstrdup(comp ? comp : defprop[i]);
+ break;
+ case PROPOSAL_SERVER_HOST_KEY_ALGS:
+ prop[i] = xstrdup(hkalgs ? hkalgs : defprop[i]);
+ break;
+ default:
+ prop[i] = xstrdup(defprop[i]);
+ }
+ }
+}
+
+void
+kex_proposal_free_entries(char *prop[PROPOSAL_MAX])
+{
+ u_int i;
+
+ for (i = 0; i < PROPOSAL_MAX; i++)
+ free(prop[i]);
+}
+
/* Validate GSS KEX method name list */
int
kex_gss_names_valid(const char *names)
diff --git a/kex.h b/kex.h
index 8b95227..87ba7c8 100644
--- a/kex.h
+++ b/kex.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: kex.h,v 1.114 2021/01/31 22:55:29 djm Exp $ */
+/* $OpenBSD: kex.h,v 1.118 2023/03/06 12:14:48 dtucker Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@@ -192,6 +192,9 @@ char *kex_alg_list(char);
char *kex_gss_alg_list(char);
char *kex_names_cat(const char *, const char *);
int kex_assemble_names(char **, const char *, const char *);
+void kex_proposal_populate_entries(struct ssh *, char *prop[PROPOSAL_MAX],
+ const char *, const char *, const char *, const char *, const char *);
+void kex_proposal_free_entries(char *prop[PROPOSAL_MAX]);
int kex_gss_names_valid(const char *);
int kex_exchange_identification(struct ssh *, int, const char *);
diff --git a/sshconnect2.c b/sshconnect2.c
index eb0df92..9267534 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect2.c,v 1.359 2022/07/01 03:39:44 dtucker Exp $ */
+/* $OpenBSD: sshconnect2.c,v 1.364 2023/03/06 12:14:48 dtucker Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Damien Miller. All rights reserved.
@@ -58,7 +58,6 @@
#include "cipher.h"
#include "sshkey.h"
#include "kex.h"
-#include "myproposal.h"
#include "sshconnect.h"
#include "authfile.h"
#include "dh.h"
@@ -216,11 +215,9 @@ void
ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port,
const struct ssh_conn_info *cinfo)
{
- char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT };
- char *s, *all_key;
- char *prop_kex = NULL, *prop_enc = NULL, *prop_hostkey = NULL;
- int r, use_known_hosts_order = 0;
-
+ char *myproposal[PROPOSAL_MAX];
+ char *s, *all_key, *hkalgs = NULL;
+ int r;
#if defined(GSSAPI) && defined(WITH_OPENSSL)
char *orig = NULL, *gss = NULL;
char *gss_host = NULL;
@@ -230,15 +227,9 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port,
xxx_hostaddr = hostaddr;
xxx_conn_info = cinfo;
- /*
- * If the user has not specified HostkeyAlgorithms, or has only
- * appended or removed algorithms from that list then prefer algorithms
- * that are in the list that are supported by known_hosts keys.
- */
- if (options.hostkeyalgorithms == NULL ||
- options.hostkeyalgorithms[0] == '-' ||
- options.hostkeyalgorithms[0] == '+')
- use_known_hosts_order = 1;
+ if (options.rekey_limit || options.rekey_interval)
+ ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
+ options.rekey_interval);
/* Expand or fill in HostkeyAlgorithms */
all_key = sshkey_alg_list(0, 0, 1, ',');
@@ -249,25 +240,22 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port,
if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL)
fatal_f("kex_names_cat");
- myproposal[PROPOSAL_KEX_ALGS] = prop_kex = compat_kex_proposal(ssh, s);
- myproposal[PROPOSAL_ENC_ALGS_CTOS] =
- myproposal[PROPOSAL_ENC_ALGS_STOC] = prop_enc =
- compat_cipher_proposal(ssh, options.ciphers);
- myproposal[PROPOSAL_COMP_ALGS_CTOS] =
- myproposal[PROPOSAL_COMP_ALGS_STOC] =
- (char *)compression_alg_list(options.compression);
- myproposal[PROPOSAL_MAC_ALGS_CTOS] =
- myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
- if (use_known_hosts_order) {
- /* Query known_hosts and prefer algorithms that appear there */
- myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = prop_hostkey =
- compat_pkalg_proposal(ssh,
- order_hostkeyalgs(host, hostaddr, port, cinfo));
- } else {
- /* Use specified HostkeyAlgorithms exactly */
- myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = prop_hostkey =
- compat_pkalg_proposal(ssh, options.hostkeyalgorithms);
- }
+
+ /*
+ * If the user has not specified HostkeyAlgorithms, or has only
+ * appended or removed algorithms from that list then prefer algorithms
+ * that are in the list that are supported by known_hosts keys.
+ */
+ if (options.hostkeyalgorithms == NULL ||
+ options.hostkeyalgorithms[0] == '-' ||
+ options.hostkeyalgorithms[0] == '+')
+ hkalgs = order_hostkeyalgs(host, hostaddr, port, cinfo);
+
+ kex_proposal_populate_entries(ssh, myproposal, s, options.ciphers,
+ options.macs, compression_alg_list(options.compression),
+ hkalgs ? hkalgs : options.hostkeyalgorithms);
+
+ free(hkalgs);
#if defined(GSSAPI) && defined(WITH_OPENSSL)
if (options.gss_keyex) {
@@ -310,10 +298,6 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port,
}
#endif
- if (options.rekey_limit || options.rekey_interval)
- ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
- options.rekey_interval);
-
/* start key exchange */
if ((r = kex_setup(ssh, myproposal)) != 0)
fatal_r(r, "kex_setup");
@@ -357,6 +341,7 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port,
ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done);
/* remove ext-info from the KEX proposals for rekeying */
+ free(myproposal[PROPOSAL_KEX_ALGS]);
myproposal[PROPOSAL_KEX_ALGS] =
compat_kex_proposal(ssh, options.kex_algorithms);
#if defined(GSSAPI) && defined(WITH_OPENSSL)
@@ -380,10 +365,7 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port,
(r = ssh_packet_write_wait(ssh)) != 0)
fatal_fr(r, "send packet");
#endif
- /* Free only parts of proposal that were dynamically allocated here. */
- free(prop_kex);
- free(prop_enc);
- free(prop_hostkey);
+ kex_proposal_free_entries(myproposal);
}
/*
diff --git a/sshd.c b/sshd.c
index dd7e1d4..aa3c3b7 100644
--- a/sshd.c
+++ b/sshd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshd.c,v 1.589 2022/07/01 03:39:44 dtucker Exp $ */
+/* $OpenBSD: sshd.c,v 1.599 2023/03/06 12:14:48 dtucker Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -107,7 +107,6 @@
#include "digest.h"
#include "sshkey.h"
#include "kex.h"
-#include "myproposal.h"
#include "authfile.h"
#include "pathnames.h"
#include "atomicio.h"
@@ -2524,33 +2523,23 @@ sshd_hostkey_sign(struct ssh *ssh, struct sshkey *privkey,
static void
do_ssh2_kex(struct ssh *ssh)
{
- char *myproposal[PROPOSAL_MAX] = { KEX_SERVER };
+ char *hkalgs = NULL, *myproposal[PROPOSAL_MAX];
+ const char *compression = NULL;
struct kex *kex;
- char *prop_kex = NULL, *prop_enc = NULL, *prop_hostkey = NULL;
int r;
- myproposal[PROPOSAL_KEX_ALGS] = prop_kex = compat_kex_proposal(ssh,
- options.kex_algorithms);
- myproposal[PROPOSAL_ENC_ALGS_CTOS] =
- myproposal[PROPOSAL_ENC_ALGS_STOC] = prop_enc =
- compat_cipher_proposal(ssh, options.ciphers);
- myproposal[PROPOSAL_ENC_ALGS_STOC] = compat_cipher_proposal(ssh,
- options.ciphers);
- myproposal[PROPOSAL_MAC_ALGS_CTOS] =
- myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
-
- if (options.compression == COMP_NONE) {
- myproposal[PROPOSAL_COMP_ALGS_CTOS] =
- myproposal[PROPOSAL_COMP_ALGS_STOC] = "none";
- }
-
if (options.rekey_limit || options.rekey_interval)
ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
options.rekey_interval);
- /* coverity[leaked_storage : FALSE]*/
- myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = prop_hostkey =
- compat_pkalg_proposal(ssh, list_hostkey_types());
+ if (options.compression == COMP_NONE)
+ compression = "none";
+ hkalgs = list_hostkey_types();
+
+ kex_proposal_populate_entries(ssh, myproposal, options.kex_algorithms,
+ options.ciphers, options.macs, compression, hkalgs);
+
+ free(hkalgs);
#if defined(GSSAPI) && defined(WITH_OPENSSL)
{
char *orig;
@@ -2645,9 +2634,7 @@ do_ssh2_kex(struct ssh *ssh)
(r = ssh_packet_write_wait(ssh)) != 0)
fatal_fr(r, "send test");
#endif
- free(prop_kex);
- free(prop_enc);
- free(prop_hostkey);
+ kex_proposal_free_entries(myproposal);
debug("KEX done");
}
--
2.33.0

View File

@ -6,7 +6,7 @@
%{?no_gtk2:%global gtk2 0}
%global sshd_uid 74
%global openssh_release 22
%global openssh_release 23
Name: openssh
Version: 8.8p1
@ -112,6 +112,10 @@ Patch79: set-ssh-config.patch
Patch80: backport-upstream-CVE-2023-25136-fix-double-free-caused.patch
Patch81: backport-fix-CVE-2023-38408-upstream-terminate-process.patch
Patch82: backport-upstream-In-channel_request_remote_forwarding-the-pa.patch
Patch83: backport-upstream-Refactor-creation-of-KEX-proposal.patch
Patch84: backport-upstream-Limit-number-of-entries-in-SSH2_MSG_EXT_INF.patch
Patch85: backport-CVE-2023-48795-upstream-implement-strict-key-exchange-in-ssh-and-ss.patch
Patch86: backport-CVE-2023-51385-upstream-ban-user-hostnames-with-most-shell-metachar.patch
Requires: /sbin/nologin
Requires: libselinux >= 2.3-5 audit-libs >= 1.0.8
@ -273,6 +277,10 @@ popd
%patch80 -p1
%patch81 -p1
%patch82 -p1
%patch83 -p1
%patch84 -p1
%patch85 -p1
%patch86 -p1
autoreconf
pushd pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4
@ -469,6 +477,12 @@ getent passwd sshd >/dev/null || \
%attr(0644,root,root) %{_mandir}/man8/sftp-server.8*
%changelog
* Sat Dec 23 2023 renmingshuai<renmingshuai@huawei.com> - 8.8p1-23
- Type:CVE
- CVE:CVE-2023-48795,CVE-2023-51385
- SUG:NA
- DESC:fix CVE-2023-48795 and CVE-2023-51385
* Tue Aug 15 2023 renmingshuai<renmingshuai@huawei.com> - 8.8p1-22
- Type:bugfix
- CVE:NA