Fix CVE-2026-60002

Fix use-after-free in cached hostkey during key re-exchange

Resolves: RHEL-193015

Signed-off-by: Zoltan Fridrich <zfridric@redhat.com>
This commit is contained in:
Zoltan Fridrich 2026-07-14 12:14:44 +02:00
parent 96e0821266
commit 1c775f8203
2 changed files with 196 additions and 0 deletions

View File

@ -0,0 +1,191 @@
diff --color -ruNp a/ssh.c b/ssh.c
--- a/ssh.c 2026-07-14 11:58:09.036496915 +0200
+++ b/ssh.c 2026-07-14 12:00:55.044542741 +0200
@@ -619,26 +619,6 @@ set_addrinfo_port(struct addrinfo *addrs
}
}
-static void
-ssh_conn_info_free(struct ssh_conn_info *cinfo)
-{
- if (cinfo == NULL)
- return;
- free(cinfo->conn_hash_hex);
- free(cinfo->shorthost);
- free(cinfo->uidstr);
- free(cinfo->keyalias);
- free(cinfo->thishost);
- free(cinfo->host_arg);
- free(cinfo->portstr);
- free(cinfo->remhost);
- free(cinfo->remuser);
- free(cinfo->homedir);
- free(cinfo->locuser);
- free(cinfo->jmphost);
- free(cinfo);
-}
-
/*
* Main program for the ssh client.
*/
@@ -1765,8 +1745,8 @@ main(int ac, char **av)
ssh_signal(SIGCHLD, main_sigchld_handler);
/* Log into the remote system. Never returns if the login fails. */
- ssh_login(ssh, &sensitive_data, host, (struct sockaddr *)&hostaddr,
- options.port, pw, timeout_ms, cinfo);
+ ssh_login(ssh, &sensitive_data, host, &hostaddr, options.port,
+ pw, timeout_ms, cinfo);
/* We no longer need the private host keys. Clear them now. */
if (sensitive_data.nkeys != 0) {
diff --color -ruNp a/sshconnect2.c b/sshconnect2.c
--- a/sshconnect2.c 2026-07-14 11:58:09.031477028 +0200
+++ b/sshconnect2.c 2026-07-14 12:01:56.374803123 +0200
@@ -89,7 +89,7 @@ extern Options options;
*/
static char *xxx_host;
-static struct sockaddr *xxx_hostaddr;
+static struct sockaddr_storage xxx_hostaddr;
static const struct ssh_conn_info *xxx_conn_info;
static int key_type_allowed(struct sshkey *, const char *);
@@ -105,7 +105,7 @@ verify_host_key_callback(struct sshkey *
fatal("Server host key %s not in HostKeyAlgorithms",
sshkey_ssh_name(hostkey));
}
- if (verify_host_key(xxx_host, xxx_hostaddr, hostkey,
+ if (verify_host_key(xxx_host, (struct sockaddr *)&xxx_hostaddr, hostkey,
xxx_conn_info) != 0)
fatal("Host key verification failed.");
return 0;
@@ -222,8 +222,8 @@ order_hostkeyalgs(char *host, struct soc
}
void
-ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port,
- const struct ssh_conn_info *cinfo)
+ssh_kex2(struct ssh *ssh, char *host, struct sockaddr_storage *hostaddr,
+ u_short port, const struct ssh_conn_info *cinfo)
{
char *myproposal[PROPOSAL_MAX];
char *all_key, *hkalgs = NULL, *filtered_algs = NULL;
@@ -234,9 +234,9 @@ ssh_kex2(struct ssh *ssh, char *host, st
char *gss_host = NULL;
#endif
- xxx_host = host;
- xxx_hostaddr = hostaddr;
- xxx_conn_info = cinfo;
+ xxx_host = xstrdup(host);
+ xxx_hostaddr = *hostaddr;
+ xxx_conn_info = ssh_conn_info_dup(cinfo);
if (options.rekey_limit || options.rekey_interval)
ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
@@ -259,8 +259,10 @@ ssh_kex2(struct ssh *ssh, char *host, st
fatal_fr(r, "kex_assemble_namelist");
free(all_key);
- if (use_known_hosts_order)
- hkalgs = order_hostkeyalgs(host, hostaddr, port, cinfo);
+ if (use_known_hosts_order) {
+ hkalgs = order_hostkeyalgs(host, (struct sockaddr *)hostaddr,
+ port, cinfo);
+ }
filtered_algs = hkalgs ? match_filter_allowlist(hkalgs, options.pubkey_accepted_algos)
: match_filter_allowlist(options.hostkeyalgorithms,
diff --color -ruNp a/sshconnect.c b/sshconnect.c
--- a/sshconnect.c 2026-07-14 11:58:08.892659011 +0200
+++ b/sshconnect.c 2026-07-14 12:00:55.045767462 +0200
@@ -84,6 +84,49 @@ extern char *__progname;
static int show_other_keys(struct hostkeys *, struct sshkey *);
static void warn_changed_key(struct sshkey *);
+void
+ssh_conn_info_free(struct ssh_conn_info *cinfo)
+{
+ if (cinfo == NULL)
+ return;
+ free(cinfo->conn_hash_hex);
+ free(cinfo->shorthost);
+ free(cinfo->uidstr);
+ free(cinfo->keyalias);
+ free(cinfo->thishost);
+ free(cinfo->host_arg);
+ free(cinfo->portstr);
+ free(cinfo->remhost);
+ free(cinfo->remuser);
+ free(cinfo->homedir);
+ free(cinfo->locuser);
+ free(cinfo->jmphost);
+ freezero(cinfo, sizeof(*cinfo));
+}
+
+struct ssh_conn_info *
+ssh_conn_info_dup(const struct ssh_conn_info *cinfo)
+{
+ struct ssh_conn_info *ret;
+
+ if (cinfo == NULL)
+ return NULL;
+ ret = xcalloc(1, sizeof(*ret));
+ ret->conn_hash_hex = xstrdup(cinfo->conn_hash_hex);
+ ret->shorthost = xstrdup(cinfo->shorthost);
+ ret->uidstr = xstrdup(cinfo->uidstr);
+ ret->keyalias = xstrdup(cinfo->keyalias);
+ ret->thishost = xstrdup(cinfo->thishost);
+ ret->host_arg = xstrdup(cinfo->host_arg);
+ ret->portstr = xstrdup(cinfo->portstr);
+ ret->remhost = xstrdup(cinfo->remhost);
+ ret->remuser = xstrdup(cinfo->remuser);
+ ret->homedir = xstrdup(cinfo->homedir);
+ ret->locuser = xstrdup(cinfo->locuser);
+ ret->jmphost = xstrdup(cinfo->jmphost);
+ return ret;
+}
+
/* Expand a proxy command */
static char *
expand_proxy_command(const char *proxy_command, const char *user,
@@ -1589,8 +1632,8 @@ out:
*/
void
ssh_login(struct ssh *ssh, Sensitive *sensitive, const char *orighost,
- struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms,
- const struct ssh_conn_info *cinfo)
+ struct sockaddr_storage *hostaddr, u_short port, struct passwd *pw,
+ int timeout_ms, const struct ssh_conn_info *cinfo)
{
char *host;
char *server_user, *local_user;
diff --color -ruNp a/sshconnect.h b/sshconnect.h
--- a/sshconnect.h 2024-09-20 00:20:48.000000000 +0200
+++ b/sshconnect.h 2026-07-14 12:00:55.045948953 +0200
@@ -73,7 +73,7 @@ int ssh_connect(struct ssh *, const cha
void ssh_kill_proxy_command(void);
void ssh_login(struct ssh *, Sensitive *, const char *,
- struct sockaddr *, u_short, struct passwd *, int,
+ struct sockaddr_storage *, u_short, struct passwd *, int,
const struct ssh_conn_info *);
int verify_host_key(char *, struct sockaddr *, struct sshkey *,
@@ -82,7 +82,7 @@ int verify_host_key(char *, struct sock
void get_hostfile_hostname_ipaddr(char *, struct sockaddr *, u_short,
char **, char **);
-void ssh_kex2(struct ssh *ssh, char *, struct sockaddr *, u_short,
+void ssh_kex2(struct ssh *ssh, char *, struct sockaddr_storage *, u_short,
const struct ssh_conn_info *);
void ssh_userauth2(struct ssh *ssh, const char *, const char *,
@@ -98,3 +98,6 @@ void load_hostkeys_command(struct hostk
const struct sshkey *, const char *);
int hostkey_accepted_by_hostkeyalgs(const struct sshkey *);
+
+void ssh_conn_info_free(struct ssh_conn_info *);
+struct ssh_conn_info *ssh_conn_info_dup(const struct ssh_conn_info *);

View File

@ -254,6 +254,8 @@ Patch1043: openssh-9.9p1-authorized-keys-principles-option.patch
Patch1044: openssh-9.9p1-proxyjump-username-validity-checks.patch
# upstream 36480181fa22f98e180b4f9e10203480c0346c78
Patch1045: openssh-9.9p1-scp-remote-glob.patch
# upstream e8bdfb151a356d0171fea4194dd205fbb252be23
Patch1046: openssh-9.9p1-cve-2026-60002.patch
License: BSD-3-Clause AND BSD-2-Clause AND ISC AND SSH-OpenSSH AND ssh-keyscan AND snprintf AND LicenseRef-Fedora-Public-Domain AND X11-distribute-modifications-variant
Requires: /sbin/nologin
@ -463,6 +465,7 @@ gpgv2 --quiet --keyring %{SOURCE3} %{SOURCE1} %{SOURCE0}
%patch -P 1043 -p1 -b .authorized-keys-principles-option
%patch -P 1044 -p1 -b .proxyjump-username-validity-checks
%patch -P 1045 -p1 -b .scp-remote-glob
%patch -P 1046 -p1 -b .cve-2026-60002
%patch -P 100 -p1 -b .coverity
@ -757,6 +760,8 @@ test -f %{sysconfig_anaconda} && \
in unintended parent directories when scp performs remote-to-remote copy
via the local host
Resolves: RHEL-193172
- CVE-2026-60002: Fix use-after-free in cached hostkey during key re-exchange
Resolves: RHEL-193015
* Tue Apr 14 2026 Dmitry Belyavskiy <dbelyavs@redhat.com> - 9.9p1-26
- Improve keytab detection when obtaining Kerberos tickets on behalf of user on SSH authentication