From e8bdfb151a356d0171fea4194dd205fbb252be23 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Mon, 6 Jul 2026 07:49:58 +0000 Subject: [PATCH] upstream: fix ownership and lifetime of several bits of client state that need to persist for the life of the connection, especially the cached hostkey that was being incorrectly freed early on some paths, possibly allowing its use after free. Reported by Zhenpeng (Leo) Lin from depthfirst.com OpenBSD-Commit-ID: faaa6ad72e7d69d41fa8b197b606265b7d9bc73f CVE: CVE-2026-60002 Upstream: https://github.com/openssh/openssh-portable/commit/e8bdfb151a356d0171fea4194dd205fbb252be23 Signed-off-by: Thomas Perale --- ssh.c | 26 +++----------------------- sshconnect.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- sshconnect.h | 9 ++++++--- sshconnect2.c | 22 ++++++++++++---------- 4 files changed, 67 insertions(+), 39 deletions(-) diff --git a/ssh.c b/ssh.c index 5a160c8c1352..7c644c23c214 100644 --- a/ssh.c +++ b/ssh.c @@ -606,26 +606,6 @@ set_addrinfo_port(struct addrinfo *addrs, int port) } } -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. */ @@ -1744,8 +1724,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 --git a/sshconnect.c b/sshconnect.c index 2fc1f6026eb2..0ddfc76b367b 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -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 @@ warn_nonpq_kex(void) */ 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 --git a/sshconnect.h b/sshconnect.h index 4c19490da487..2ac2c07e005a 100644 --- a/sshconnect.h +++ b/sshconnect.h @@ -73,7 +73,7 @@ int ssh_connect(struct ssh *, const char *, const char *, 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 sockaddr *, struct sshkey *, 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 hostkeys *, const char *, 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 *); diff --git a/sshconnect2.c b/sshconnect2.c index ecb4ae524ee7..d1555ee97274 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -87,7 +87,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 *); @@ -103,7 +103,7 @@ verify_host_key_callback(struct sshkey *hostkey, struct ssh *ssh) 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; @@ -220,16 +220,16 @@ order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port, } 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; int r, use_known_hosts_order = 0; - 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, @@ -252,8 +252,10 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port, 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); + } kex_proposal_populate_entries(ssh, myproposal, options.kex_algorithms, options.ciphers, options.macs,