From 82b359a76ecc9688e0125cfb0f4db94b37d99023 Mon Sep 17 00:00:00 2001 From: Dusty Mabe Date: Thu, 2 Mar 2023 10:17:58 -0500 Subject: [PATCH 01/11] update date in changelog entry This entry is out of chronological order, which means we get a warning/error every time. I'm just updating here to the commitdate of the commit, which puts everything back in chronological order. --- openssh.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openssh.spec b/openssh.spec index 1655d76..dd5fa64 100644 --- a/openssh.spec +++ b/openssh.spec @@ -720,7 +720,7 @@ test -f %{sysconfig_anaconda} && \ %endif %changelog -* Fri Dec 02 2022 Dmitry Belyavskiy - 9.0p1-10 +* Mon Jan 23 2023 Dmitry Belyavskiy - 9.0p1-10 - Restore upstream behaviour and default host key permissions (rhbz#2141272) * Thu Jan 19 2023 Fedora Release Engineering - 9.0p1-9.1 From ff60be4082db1b6398eab8cfcb36bb92251063c7 Mon Sep 17 00:00:00 2001 From: Dusty Mabe Date: Mon, 30 Jan 2023 16:01:17 +0100 Subject: [PATCH 02/11] Use a service unit to strip ssh_keys group from host keys (rhbz#2172956) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a systemd service unit to strip the ssh_keys group and change the mode for host keys. This ensure that this migration is done right before the openssh server startup on all kind of systems, either RPM or rpm-ostree based. Use a marker file to only do this once. We need to keep this service unit for two Fedora releases so we will be able to remove it in Fedora 40. See: https://fedoraproject.org/wiki/Changes/SSHKeySignSuidBit Fixes: 7a21555 Get rid of ssh_keys group for new installations Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2172956 Co-authored-by: Timothée Ravier --- openssh.spec | 27 +++++++++++++++++------ ssh-host-keys-migration.service | 15 +++++++++++++ ssh-host-keys-migration.sh | 38 +++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 ssh-host-keys-migration.service create mode 100644 ssh-host-keys-migration.sh diff --git a/openssh.spec b/openssh.spec index dd5fa64..2b5a3ec 100644 --- a/openssh.spec +++ b/openssh.spec @@ -47,7 +47,7 @@ # Do not forget to bump pam_ssh_agent_auth release if you rewind the main package release to 1 %global openssh_ver 9.0p1 -%global openssh_rel 10 +%global openssh_rel 11 %global pam_ssh_agent_ver 0.10.4 %global pam_ssh_agent_rel 7 @@ -74,6 +74,8 @@ Source15: sshd-keygen.target Source16: ssh-agent.service Source17: ssh-agent.socket Source19: openssh-server-systemd-sysusers.conf +Source20: ssh-host-keys-migration.sh +Source21: ssh-host-keys-migration.service #https://bugzilla.mindrot.org/show_bug.cgi?id=2581 Patch100: openssh-6.7p1-coverity.patch @@ -582,6 +584,10 @@ install -m755 contrib/ssh-copy-id $RPM_BUILD_ROOT%{_bindir}/ install contrib/ssh-copy-id.1 $RPM_BUILD_ROOT%{_mandir}/man1/ install -d -m711 ${RPM_BUILD_ROOT}/%{_datadir}/empty.sshd install -p -D -m 0644 %{SOURCE19} %{buildroot}%{_sysusersdir}/openssh-server.conf +# Migration service/script for Fedora 38 change to remove group ownership for standard host keys +# See https://fedoraproject.org/wiki/Changes/SSHKeySignSuidBit +install -m744 %{SOURCE20} $RPM_BUILD_ROOT/%{_libexecdir}/openssh/ssh-host-keys-migration.sh +install -m644 %{SOURCE21} $RPM_BUILD_ROOT/%{_unitdir}/ssh-host-keys-migration.service # enabled in 90-default.preset %if ! %{no_gnome_askpass} install contrib/gnome-ssh-askpass $RPM_BUILD_ROOT%{_libexecdir}/openssh/gnome-ssh-askpass @@ -608,13 +614,16 @@ popd %pre server %sysusers_create_compat %{SOURCE19} -# Migration scriptlet for Fedora 38/39 -# We want to remove group ownership for standard host keys if they exist -test -f /etc/ssh/ssh_host_rsa_key && /usr/bin/chmod g-r /etc/ssh/ssh_host_rsa_key || : -test -f /etc/ssh/ssh_host_ecdsa_key && /usr/bin/chmod g-r /etc/ssh/ssh_host_ecdsa_key || : -test -f /etc/ssh/ssh_host_ed25519_key && /usr/bin/chmod g-r /etc/ssh/ssh_host_ed25519_key || : %post server +if [ $1 -gt 1 ]; then + # In the case of an upgrade (never true on OSTree systems) run the migration + # script for Fedora 38 to remove group ownership for host keys. + %{_libexecdir}/openssh/ssh-host-keys-migration.sh + # Prevent the systemd unit that performs the same service (useful for + # OSTree systems) from running. + touch /var/lib/.ssh-host-keys-migration +fi %systemd_post sshd.service sshd.socket # Migration scriptlet for Fedora 31 and 32 installations to sshd_config # drop-in directory (in F32+). @@ -699,6 +708,8 @@ test -f %{sysconfig_anaconda} && \ %attr(0644,root,root) %{_unitdir}/sshd-keygen@.service %attr(0644,root,root) %{_unitdir}/sshd-keygen.target %attr(0644,root,root) %{_sysusersdir}/openssh-server.conf +%attr(0644,root,root) %{_unitdir}/ssh-host-keys-migration.service +%attr(0744,root,root) %{_libexecdir}/openssh/ssh-host-keys-migration.sh %files keycat %doc HOWTO.ssh-keycat @@ -720,6 +731,10 @@ test -f %{sysconfig_anaconda} && \ %endif %changelog +* Wed Mar 01 2023 Dusty Mabe - 9.0p1-11 +- Provide a systemd unit for restoring default host key permissions (rhbz#2172956) +- Co-Authored by Timothée Ravier + * Mon Jan 23 2023 Dmitry Belyavskiy - 9.0p1-10 - Restore upstream behaviour and default host key permissions (rhbz#2141272) diff --git a/ssh-host-keys-migration.service b/ssh-host-keys-migration.service new file mode 100644 index 0000000..41e2c6b --- /dev/null +++ b/ssh-host-keys-migration.service @@ -0,0 +1,15 @@ +[Unit] +Description=Update OpenSSH host key permissions +Documentation=https://fedoraproject.org/wiki/Changes/SSHKeySignSuidBit +Before=sshd.service +After=ssh-keygen.target +ConditionPathExists=!/var/lib/.ssh-host-keys-migration + +[Service] +Type=oneshot +ExecStart=-/usr/libexec/openssh/ssh-host-keys-migration.sh +ExecStart=touch /var/lib/.ssh-host-keys-migration +RemainAfterExit=yes + +[Install] +WantedBy=sshd.service diff --git a/ssh-host-keys-migration.sh b/ssh-host-keys-migration.sh new file mode 100644 index 0000000..f01ab86 --- /dev/null +++ b/ssh-host-keys-migration.sh @@ -0,0 +1,38 @@ +#!/usr/bin/bash +set -eu -o pipefail +# Detect existing non-conforming host keys and perform the permissions migration +# https://fedoraproject.org/wiki/Changes/SSHKeySignSuidBit +# +# Example output looks like: +# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +# @ WARNING: UNPROTECTED PRIVATE KEY FILE! @ +# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +# Permissions 0640 for '/etc/ssh/ssh_host_rsa_key' are too open. +# It is required that your private key files are NOT accessible by others. +# This private key will be ignored. +# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +# @ WARNING: UNPROTECTED PRIVATE KEY FILE! @ +# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +# Permissions 0640 for '/etc/ssh/ssh_host_ecdsa_key' are too open. +# It is required that your private key files are NOT accessible by others. +# This private key will be ignored. +# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +# @ WARNING: UNPROTECTED PRIVATE KEY FILE! @ +# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +# Permissions 0640 for '/etc/ssh/ssh_host_ed25519_key' are too open. +# It is required that your private key files are NOT accessible by others. +# This private key will be ignored. +# sshd: no hostkeys available -- exiting. +# +output="$(sshd -T 2>&1 || true)" # expected to fail +if grep -q "sshd: no hostkeys available" <<< "$output"; then + while read line; do + if [[ $line =~ ^Permissions\ [0-9]+\ for\ \'(.*)\'\ are\ too\ open. ]]; then + keyfile=${BASH_REMATCH[1]} + echo $line + echo -e "\t-> changing permissions on $keyfile" + chmod --verbose g-r $keyfile + chown --verbose root:root $keyfile + fi + done <<< "$output" +fi From 2d651fde7deabc2866474bf0ce0459406ef55f42 Mon Sep 17 00:00:00 2001 From: Dusty Mabe Date: Sun, 5 Mar 2023 22:48:30 -0500 Subject: [PATCH 03/11] Mark /var/lib/.ssh-host-keys-migration as %ghost file --- openssh.spec | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openssh.spec b/openssh.spec index 2b5a3ec..fb52c37 100644 --- a/openssh.spec +++ b/openssh.spec @@ -47,7 +47,7 @@ # Do not forget to bump pam_ssh_agent_auth release if you rewind the main package release to 1 %global openssh_ver 9.0p1 -%global openssh_rel 11 +%global openssh_rel 12 %global pam_ssh_agent_ver 0.10.4 %global pam_ssh_agent_rel 7 @@ -588,6 +588,8 @@ install -p -D -m 0644 %{SOURCE19} %{buildroot}%{_sysusersdir}/openssh-server.con # See https://fedoraproject.org/wiki/Changes/SSHKeySignSuidBit install -m744 %{SOURCE20} $RPM_BUILD_ROOT/%{_libexecdir}/openssh/ssh-host-keys-migration.sh install -m644 %{SOURCE21} $RPM_BUILD_ROOT/%{_unitdir}/ssh-host-keys-migration.service # enabled in 90-default.preset +install -d $RPM_BUILD_ROOT/%{_localstatedir}/lib +touch $RPM_BUILD_ROOT/%{_localstatedir}/lib/.ssh-host-keys-migration %if ! %{no_gnome_askpass} install contrib/gnome-ssh-askpass $RPM_BUILD_ROOT%{_libexecdir}/openssh/gnome-ssh-askpass @@ -710,6 +712,7 @@ test -f %{sysconfig_anaconda} && \ %attr(0644,root,root) %{_sysusersdir}/openssh-server.conf %attr(0644,root,root) %{_unitdir}/ssh-host-keys-migration.service %attr(0744,root,root) %{_libexecdir}/openssh/ssh-host-keys-migration.sh +%ghost %attr(0644,root,root) %{_localstatedir}/lib/.ssh-host-keys-migration %files keycat %doc HOWTO.ssh-keycat @@ -731,6 +734,9 @@ test -f %{sysconfig_anaconda} && \ %endif %changelog +* Mon Mar 06 2023 Dusty Mabe - 9.0p1-12 +- Mark /var/lib/.ssh-host-keys-migration as %ghost file + * Wed Mar 01 2023 Dusty Mabe - 9.0p1-11 - Provide a systemd unit for restoring default host key permissions (rhbz#2172956) - Co-Authored by Timothée Ravier From f8a488880c69db8ce2256f25e425d6b6c59c641d Mon Sep 17 00:00:00 2001 From: Dusty Mabe Date: Sun, 5 Mar 2023 22:51:10 -0500 Subject: [PATCH 04/11] Make ssh-host key migration less conditional If there is a case where some host keys don't have correct permissions then they won't get migrated. Let's make the migration script attempt migration for the rest of the keys too. --- openssh.spec | 1 + ssh-host-keys-migration.sh | 20 +++++++++----------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/openssh.spec b/openssh.spec index fb52c37..b88a098 100644 --- a/openssh.spec +++ b/openssh.spec @@ -736,6 +736,7 @@ test -f %{sysconfig_anaconda} && \ %changelog * Mon Mar 06 2023 Dusty Mabe - 9.0p1-12 - Mark /var/lib/.ssh-host-keys-migration as %ghost file +- Make ssh-host key migration less conditional * Wed Mar 01 2023 Dusty Mabe - 9.0p1-11 - Provide a systemd unit for restoring default host key permissions (rhbz#2172956) diff --git a/ssh-host-keys-migration.sh b/ssh-host-keys-migration.sh index f01ab86..083326e 100644 --- a/ssh-host-keys-migration.sh +++ b/ssh-host-keys-migration.sh @@ -25,14 +25,12 @@ set -eu -o pipefail # sshd: no hostkeys available -- exiting. # output="$(sshd -T 2>&1 || true)" # expected to fail -if grep -q "sshd: no hostkeys available" <<< "$output"; then - while read line; do - if [[ $line =~ ^Permissions\ [0-9]+\ for\ \'(.*)\'\ are\ too\ open. ]]; then - keyfile=${BASH_REMATCH[1]} - echo $line - echo -e "\t-> changing permissions on $keyfile" - chmod --verbose g-r $keyfile - chown --verbose root:root $keyfile - fi - done <<< "$output" -fi +while read line; do + if [[ $line =~ ^Permissions\ [0-9]+\ for\ \'(.*)\'\ are\ too\ open. ]]; then + keyfile=${BASH_REMATCH[1]} + echo $line + echo -e "\t-> changing permissions on $keyfile" + chmod --verbose g-r $keyfile + chown --verbose root:root $keyfile + fi +done <<< "$output" From a007ed6cde8d3d3d3a31f73b3aa8acc766b23b75 Mon Sep 17 00:00:00 2001 From: Zoltan Fridrich Date: Mon, 13 Mar 2023 13:37:06 +0100 Subject: [PATCH 05/11] Add sk-dummy subpackage for test purposes Resolves: rhbz#2176795 Signed-off-by: Zoltan Fridrich --- openssh.spec | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/openssh.spec b/openssh.spec index b88a098..56ee91a 100644 --- a/openssh.spec +++ b/openssh.spec @@ -47,7 +47,7 @@ # Do not forget to bump pam_ssh_agent_auth release if you rewind the main package release to 1 %global openssh_ver 9.0p1 -%global openssh_rel 12 +%global openssh_rel 13 %global pam_ssh_agent_ver 0.10.4 %global pam_ssh_agent_rel 7 @@ -307,6 +307,10 @@ Requires: openssh = %{version}-%{release} Summary: A passphrase dialog for OpenSSH and X Requires: openssh = %{version}-%{release} +%package sk-dummy +Summary: OpenSSH SK driver for test purposes +Requires: openssh = %{version}-%{release} + %package -n pam_ssh_agent_auth Summary: PAM module for authentication with ssh-agent Version: %{pam_ssh_agent_ver} @@ -347,6 +351,9 @@ OpenSSH is a free version of SSH (Secure SHell), a program for logging into and executing commands on a remote machine. This package contains an X11 passphrase dialog for OpenSSH. +%description sk-dummy +This package contains a test SK driver used for OpenSSH test purposes + %description -n pam_ssh_agent_auth This package contains a PAM module which can be used to authenticate users using ssh keys stored in a ssh-agent. Through the use of the @@ -514,6 +521,7 @@ perl -pi -e "s|-lcrypto|%{_libdir}/libcrypto.a|g" Makefile %endif %make_build +make regress/misc/sk-dummy/sk-dummy.so # Define a variable to toggle gtk2/gtk3 building. This is necessary # because RPM doesn't handle nested %%if statements. @@ -614,6 +622,9 @@ pushd pam_ssh_agent_auth-pam_ssh_agent_auth-%{pam_ssh_agent_ver} popd %endif +install -m 755 -d $RPM_BUILD_ROOT%{_libdir}/sshtest/ +install -m 755 regress/misc/sk-dummy/sk-dummy.so $RPM_BUILD_ROOT%{_libdir}/sshtest + %pre server %sysusers_create_compat %{SOURCE19} @@ -726,6 +737,9 @@ test -f %{sysconfig_anaconda} && \ %attr(0755,root,root) %{_libexecdir}/openssh/ssh-askpass %endif +%files sk-dummy +%attr(0755,root,root) %{_libdir}/sshtest/sk-dummy.so + %if %{pam_ssh_agent} %files -n pam_ssh_agent_auth %license pam_ssh_agent_auth-pam_ssh_agent_auth-%{pam_ssh_agent_ver}/OPENSSH_LICENSE @@ -734,6 +748,9 @@ test -f %{sysconfig_anaconda} && \ %endif %changelog +* Mon Mar 13 2023 Zoltan Fridrich - 9.0p1-13 +- Add sk-dummy subpackage for test purposes (rhbz#2176795) + * Mon Mar 06 2023 Dusty Mabe - 9.0p1-12 - Mark /var/lib/.ssh-host-keys-migration as %ghost file - Make ssh-host key migration less conditional From 07c7ff06014aa2b99d40d8c6b6f32cc5e8d28a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Ravier?= Date: Tue, 14 Mar 2023 17:14:17 +0100 Subject: [PATCH 06/11] Make sshd & sshd@ units want ssh-host-keys-migration.service Enabling the unit via the presets does not enable it on Silverblue/Kinoite/Sericea & IoT as we don't re-preset all units like it's done in Fedora CoreOS. See: https://pagure.io/workstation-ostree-config/pull-request/246 Instead, have the sshd & sshd@ service unit `Wants` the ssh-host-keys-migration service unit so that it's pulled-in only when sshd is effectively enabled and in all cases. See: https://src.fedoraproject.org/rpms/fedora-release/pull-request/253 See: https://bugzilla.redhat.com/show_bug.cgi?id=2172956 See: https://src.fedoraproject.org/rpms/fedora-release/pull-request/252 --- openssh.spec | 8 ++++++-- sshd.service | 3 +++ sshd@.service | 3 +++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/openssh.spec b/openssh.spec index 56ee91a..e64875d 100644 --- a/openssh.spec +++ b/openssh.spec @@ -47,7 +47,7 @@ # Do not forget to bump pam_ssh_agent_auth release if you rewind the main package release to 1 %global openssh_ver 9.0p1 -%global openssh_rel 13 +%global openssh_rel 14 %global pam_ssh_agent_ver 0.10.4 %global pam_ssh_agent_rel 7 @@ -595,7 +595,8 @@ install -p -D -m 0644 %{SOURCE19} %{buildroot}%{_sysusersdir}/openssh-server.con # Migration service/script for Fedora 38 change to remove group ownership for standard host keys # See https://fedoraproject.org/wiki/Changes/SSHKeySignSuidBit install -m744 %{SOURCE20} $RPM_BUILD_ROOT/%{_libexecdir}/openssh/ssh-host-keys-migration.sh -install -m644 %{SOURCE21} $RPM_BUILD_ROOT/%{_unitdir}/ssh-host-keys-migration.service # enabled in 90-default.preset +# Pulled-in via a `Wants=` in `sshd.service` & `sshd@.service` +install -m644 %{SOURCE21} $RPM_BUILD_ROOT/%{_unitdir}/ssh-host-keys-migration.service install -d $RPM_BUILD_ROOT/%{_localstatedir}/lib touch $RPM_BUILD_ROOT/%{_localstatedir}/lib/.ssh-host-keys-migration @@ -748,6 +749,9 @@ test -f %{sysconfig_anaconda} && \ %endif %changelog +* Tue Mar 14 2023 Timothée Ravier - 9.0p1-14 +- Make sshd & sshd@ units want ssh-host-keys-migration.service + * Mon Mar 13 2023 Zoltan Fridrich - 9.0p1-13 - Add sk-dummy subpackage for test purposes (rhbz#2176795) diff --git a/sshd.service b/sshd.service index e8afb86..0cb2a26 100644 --- a/sshd.service +++ b/sshd.service @@ -3,6 +3,9 @@ Description=OpenSSH server daemon Documentation=man:sshd(8) man:sshd_config(5) After=network.target sshd-keygen.target Wants=sshd-keygen.target +# Migration for Fedora 38 change to remove group ownership for standard host keys +# See https://fedoraproject.org/wiki/Changes/SSHKeySignSuidBit +Wants=ssh-host-keys-migration.service [Service] Type=notify diff --git a/sshd@.service b/sshd@.service index 196c555..be6d3b9 100644 --- a/sshd@.service +++ b/sshd@.service @@ -3,6 +3,9 @@ Description=OpenSSH per-connection server daemon Documentation=man:sshd(8) man:sshd_config(5) Wants=sshd-keygen.target After=sshd-keygen.target +# Migration for Fedora 38 change to remove group ownership for standard host keys +# See https://fedoraproject.org/wiki/Changes/SSHKeySignSuidBit +Wants=ssh-host-keys-migration.service [Service] EnvironmentFile=-/etc/sysconfig/sshd From 780a027baaa6b1691abb3808ce55473beb4046de Mon Sep 17 00:00:00 2001 From: Dmitry Belyavskiy Date: Fri, 14 Apr 2023 12:54:47 +0200 Subject: [PATCH 07/11] Avoid possible self-DoS attack Resolves: CVE-2023-25136 --- openssh-8.7p1-CVE-2023-25136.patch | 38 ++++++++++++++++++++++++++++++ openssh.spec | 13 +++++++--- 2 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 openssh-8.7p1-CVE-2023-25136.patch diff --git a/openssh-8.7p1-CVE-2023-25136.patch b/openssh-8.7p1-CVE-2023-25136.patch new file mode 100644 index 0000000..ca661ee --- /dev/null +++ b/openssh-8.7p1-CVE-2023-25136.patch @@ -0,0 +1,38 @@ +diff --git a/compat.c b/compat.c +index 46dfe3a9c2e..478a9403eea 100644 +--- a/compat.c ++++ b/compat.c +@@ -190,26 +190,26 @@ compat_pkalg_proposal(struct ssh *ssh, char *pkalg_prop) + char * + compat_kex_proposal(struct ssh *ssh, char *p) + { +- char *cp = NULL; ++ char *cp = NULL, *cp2 = NULL; + + if ((ssh->compat & (SSH_BUG_CURVE25519PAD|SSH_OLD_DHGEX)) == 0) + return xstrdup(p); + debug2_f("original KEX proposal: %s", p); + if ((ssh->compat & SSH_BUG_CURVE25519PAD) != 0) +- if ((p = match_filter_denylist(p, ++ if ((cp = match_filter_denylist(p, + "curve25519-sha256@libssh.org")) == NULL) + fatal("match_filter_denylist failed"); + if ((ssh->compat & SSH_OLD_DHGEX) != 0) { +- cp = p; +- if ((p = match_filter_denylist(p, ++ if ((cp2 = match_filter_denylist(cp ? cp : p, + "diffie-hellman-group-exchange-sha256," + "diffie-hellman-group-exchange-sha1")) == NULL) + fatal("match_filter_denylist failed"); + free(cp); ++ cp = cp2; + } +- debug2_f("compat KEX proposal: %s", p); +- if (*p == '\0') ++ if (cp == NULL || *cp == '\0') + fatal("No supported key exchange algorithms found"); +- return p; ++ debug2_f("compat KEX proposal: %s", cp); ++ return cp; + } + diff --git a/openssh.spec b/openssh.spec index e64875d..6c8c44f 100644 --- a/openssh.spec +++ b/openssh.spec @@ -47,14 +47,14 @@ # Do not forget to bump pam_ssh_agent_auth release if you rewind the main package release to 1 %global openssh_ver 9.0p1 -%global openssh_rel 14 +%global openssh_rel 15 %global pam_ssh_agent_ver 0.10.4 %global pam_ssh_agent_rel 7 Summary: An open source implementation of SSH protocol version 2 Name: openssh Version: %{openssh_ver} -Release: %{openssh_rel}%{?dist}.1 +Release: %{openssh_rel}%{?dist} URL: http://www.openssh.com/portable.html #URL1: https://github.com/jbeverly/pam_ssh_agent_auth/ Source0: ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz @@ -235,6 +235,8 @@ Patch1004: openssh-8.7p1-gssapi-auth.patch # upstream MR: # https://github.com/openssh/openssh-portable/pull/323 Patch1006: openssh-8.7p1-negotiate-supported-algs.patch +# CVE-2023-25136 +Patch1007: openssh-8.7p1-CVE-2023-25136.patch # downstream only # we skip some ssh-rsa/ssh-dss tests to make native test suite pass @@ -314,7 +316,7 @@ Requires: openssh = %{version}-%{release} %package -n pam_ssh_agent_auth Summary: PAM module for authentication with ssh-agent Version: %{pam_ssh_agent_ver} -Release: %{pam_ssh_agent_rel}.%{openssh_rel}%{?dist}.1 +Release: %{pam_ssh_agent_rel}.%{openssh_rel}%{?dist} License: BSD %description @@ -440,6 +442,7 @@ popd %patch1004 -p1 -b .gssapi-auth %patch1006 -p1 -b .negotiate-supported-algs +%patch1007 -p1 -b .cve-2023-25136 #%patch1100 -p1 -b .skipsshrsadsstests @@ -749,6 +752,10 @@ test -f %{sysconfig_anaconda} && \ %endif %changelog +* Fri Apr 14 2023 Dmitry Belyavskiy - 9.0p1-15 +- Avoid possible self-DoS attack + Resolves: CVE-2023-25136 + * Tue Mar 14 2023 Timothée Ravier - 9.0p1-14 - Make sshd & sshd@ units want ssh-host-keys-migration.service From 416a60e5959acbb1716a591001a449dee4e62424 Mon Sep 17 00:00:00 2001 From: Dmitry Belyavskiy Date: Fri, 21 Jul 2023 17:00:23 +0200 Subject: [PATCH 08/11] Fix remote code execution in ssh-agent PKCS#11 support Resolves: CVE-2023-38408 --- openssh-9.3p1-upstream-cve-2023-38408.patch | 98 +++++++++++++++++++++ openssh.spec | 8 +- 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 openssh-9.3p1-upstream-cve-2023-38408.patch diff --git a/openssh-9.3p1-upstream-cve-2023-38408.patch b/openssh-9.3p1-upstream-cve-2023-38408.patch new file mode 100644 index 0000000..0368886 --- /dev/null +++ b/openssh-9.3p1-upstream-cve-2023-38408.patch @@ -0,0 +1,98 @@ +diff --git a/ssh-agent.c b/ssh-agent.c +index 618bb198..8ea831f4 100644 +diff -up openssh-9.3p1/ssh-agent.c.cve openssh-9.3p1/ssh-agent.c +--- openssh-9.3p1/ssh-agent.c.cve 2023-07-21 15:38:13.237276580 +0200 ++++ openssh-9.3p1/ssh-agent.c 2023-07-21 15:41:30.269943569 +0200 +@@ -169,6 +169,12 @@ char socket_dir[PATH_MAX]; + /* Pattern-list of allowed PKCS#11/Security key paths */ + static char *allowed_providers; + ++/* ++ * Allows PKCS11 providers or SK keys that use non-internal providers to ++ * be added over a remote connection (identified by session-bind@openssh.com). ++ */ ++static int remote_add_provider; ++ + /* locking */ + #define LOCK_SIZE 32 + #define LOCK_SALT_SIZE 16 +@@ -1228,6 +1234,12 @@ process_add_identity(SocketEntry *e) + if (strcasecmp(sk_provider, "internal") == 0) { + debug_f("internal provider"); + } else { ++ if (e->nsession_ids != 0 && !remote_add_provider) { ++ verbose("failed add of SK provider \"%.100s\": " ++ "remote addition of providers is disabled", ++ sk_provider); ++ goto out; ++ } + if (realpath(sk_provider, canonical_provider) == NULL) { + verbose("failed provider \"%.100s\": " + "realpath: %s", sk_provider, +@@ -1368,7 +1380,7 @@ no_identities(SocketEntry *e) + + #ifdef ENABLE_PKCS11 + static char * +-sanitize_pkcs11_provider(const char *provider) ++sanitize_pkcs11_provider(SocketEntry *e, const char *provider) + { + struct pkcs11_uri *uri = NULL; + char *sane_uri, *module_path = NULL; /* default path */ +@@ -1399,6 +1411,11 @@ sanitize_pkcs11_provider(const char *pro + module_path = strdup(provider); /* simple path */ + + if (module_path != NULL) { /* do not validate default NULL path in URI */ ++ if (e->nsession_ids != 0 && !remote_add_provider) { ++ verbose("failed PKCS#11 add of \"%.100s\": remote addition of " ++ "providers is disabled", provider); ++ return NULL; ++ } + if (realpath(module_path, canonical_provider) == NULL) { + verbose("failed PKCS#11 provider \"%.100s\": realpath: %s", + module_path, strerror(errno)); +@@ -1455,7 +1472,7 @@ process_add_smartcard_key(SocketEntry *e + goto send; + } + +- sane_uri = sanitize_pkcs11_provider(provider); ++ sane_uri = sanitize_pkcs11_provider(e, provider); + if (sane_uri == NULL) + goto send; + +@@ -1516,7 +1533,7 @@ process_remove_smartcard_key(SocketEntry + } + free(pin); + +- sane_uri = sanitize_pkcs11_provider(provider); ++ sane_uri = sanitize_pkcs11_provider(e, provider); + if (sane_uri == NULL) + goto send; + +@@ -2108,7 +2125,9 @@ main(int ac, char **av) + break; + case 'O': + if (strcmp(optarg, "no-restrict-websafe") == 0) +- restrict_websafe = 0; ++ restrict_websafe = 0; ++ else if (strcmp(optarg, "allow-remote-pkcs11") == 0) ++ remote_add_provider = 1; + else + fatal("Unknown -O option"); + break; +diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c +index 6be647ec..ebddf6c3 100644 +--- a/ssh-pkcs11.c ++++ b/ssh-pkcs11.c +@@ -1537,10 +1537,8 @@ pkcs11_register_provider(char *provider_id, char *pin, + error("dlopen %s failed: %s", provider_module, dlerror()); + goto fail; + } +- if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) { +- error("dlsym(C_GetFunctionList) failed: %s", dlerror()); +- goto fail; +- } ++ if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) ++ fatal("dlsym(C_GetFunctionList) failed: %s", dlerror()); + + p->module->handle = handle; + /* setup the pkcs11 callbacks */ diff --git a/openssh.spec b/openssh.spec index 6c8c44f..2b69dbc 100644 --- a/openssh.spec +++ b/openssh.spec @@ -47,7 +47,7 @@ # Do not forget to bump pam_ssh_agent_auth release if you rewind the main package release to 1 %global openssh_ver 9.0p1 -%global openssh_rel 15 +%global openssh_rel 16 %global pam_ssh_agent_ver 0.10.4 %global pam_ssh_agent_rel 7 @@ -241,6 +241,7 @@ Patch1007: openssh-8.7p1-CVE-2023-25136.patch # downstream only # we skip some ssh-rsa/ssh-dss tests to make native test suite pass #Patch1100: openssh-8.8p1-skip-some-tests.patch +Patch1015: openssh-9.3p1-upstream-cve-2023-38408.patch License: BSD Requires: /sbin/nologin @@ -445,6 +446,7 @@ popd %patch1007 -p1 -b .cve-2023-25136 #%patch1100 -p1 -b .skipsshrsadsstests +%patch -P 1015 -p1 -b .cve-2023-38408 %patch100 -p1 -b .coverity @@ -752,6 +754,10 @@ test -f %{sysconfig_anaconda} && \ %endif %changelog +* Fri Jul 21 2023 Dmitry Belyavskiy - 9.0p1-16 +- Fix remote code execution in ssh-agent PKCS#11 support + Resolves: CVE-2023-38408 + * Fri Apr 14 2023 Dmitry Belyavskiy - 9.0p1-15 - Avoid possible self-DoS attack Resolves: CVE-2023-25136 From b70f344a02fa23fb30a3ec9eb6a02255db58cdcc Mon Sep 17 00:00:00 2001 From: Mattias Ellert Date: Wed, 26 Jul 2023 23:12:23 +0200 Subject: [PATCH 09/11] Update gssapi-keyex patch for OpenSSH 9.0+ userauth_gsskeyex must have the same argument as userauth_gssapi method_gsskeyex must have the same members as method_gssapi --- openssh-8.0p1-gssapi-keyex.patch | 19 ++++++++++--------- openssh.spec | 5 ++++- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/openssh-8.0p1-gssapi-keyex.patch b/openssh-8.0p1-gssapi-keyex.patch index d5dfd7e..f72eacb 100644 --- a/openssh-8.0p1-gssapi-keyex.patch +++ b/openssh-8.0p1-gssapi-keyex.patch @@ -151,7 +151,7 @@ index 9351e042..d6446c0c 100644 + * The 'gssapi_keyex' userauth mechanism. + */ +static int -+userauth_gsskeyex(struct ssh *ssh) ++userauth_gsskeyex(struct ssh *ssh, const char *method) +{ + Authctxt *authctxt = ssh->authctxt; + int r, authenticated = 0; @@ -212,19 +212,20 @@ index 9351e042..d6446c0c 100644 else logit("GSSAPI MIC check failed"); -@@ -326,6 +370,12 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh) +@@ -326,6 +370,13 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh) return 0; } +Authmethod method_gsskeyex = { + "gssapi-keyex", ++ NULL, + userauth_gsskeyex, + &options.gss_authentication +}; + Authmethod method_gssapi = { "gssapi-with-mic", - NULL, + NULL, diff --git a/auth2.c b/auth2.c index 0e776224..1c217268 100644 --- a/auth2.c @@ -384,7 +385,7 @@ index ebd0dbca..1bdac6a4 100644 /* Do channel operations unless rekeying in progress. */ - if (!ssh_packet_is_rekeying(ssh)) + if (!ssh_packet_is_rekeying(ssh)) { - channel_after_poll(ssh, pfd, npfd_active); + channel_after_poll(ssh, pfd, npfd_active); +#ifdef GSSAPI + if (options.gss_renewal_rekey && @@ -396,8 +397,8 @@ index ebd0dbca..1bdac6a4 100644 + } + /* Buffer input from the connection. */ - if (conn_in_ready) - client_process_net_input(ssh); + if (conn_in_ready) + client_process_net_input(ssh); diff --git a/configure.ac b/configure.ac index b689db4b..efafb6bd 100644 --- a/configure.ac @@ -1370,8 +1371,8 @@ index ce85f043..574c7609 100644 +#ifdef GSSAPI + free(kex->gss_host); +#endif /* GSSAPI */ - sshbuf_free(kex->initial_sig); - sshkey_free(kex->initial_hostkey); + sshbuf_free(kex->initial_sig); + sshkey_free(kex->initial_hostkey); free(kex->failed_choice); diff --git a/kex.h b/kex.h index a5ae6ac0..fe714141 100644 @@ -3568,7 +3569,7 @@ index af00fb30..03bc87eb 100644 +# endif +#endif /* WITH_OPENSSL */ ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; - ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client; + ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client; ssh->kex->verify_host_key=&verify_host_key_callback; +#if defined(GSSAPI) && defined(WITH_OPENSSL) diff --git a/openssh.spec b/openssh.spec index 2b69dbc..f54605d 100644 --- a/openssh.spec +++ b/openssh.spec @@ -47,7 +47,7 @@ # Do not forget to bump pam_ssh_agent_auth release if you rewind the main package release to 1 %global openssh_ver 9.0p1 -%global openssh_rel 16 +%global openssh_rel 17 %global pam_ssh_agent_ver 0.10.4 %global pam_ssh_agent_rel 7 @@ -754,6 +754,9 @@ test -f %{sysconfig_anaconda} && \ %endif %changelog +* Wed Jul 26 2023 Mattias Ellert - 9.0p1-17 +- Update gssapi-keyex patch for OpenSSH 9.0+ + * Fri Jul 21 2023 Dmitry Belyavskiy - 9.0p1-16 - Fix remote code execution in ssh-agent PKCS#11 support Resolves: CVE-2023-38408 From fdfba5c681303d0097a1d6d92f55c83c1084fd4c Mon Sep 17 00:00:00 2001 From: Dmitry Belyavskiy Date: Wed, 10 Jan 2024 10:58:48 +0100 Subject: [PATCH 10/11] Forbid shell metasymbols in username/hostname Resolves: CVE-2023-51385 --- openssh-9.6p1-CVE-2023-51385.patch | 57 ++++++++++++++++++++++++++++++ openssh.spec | 12 ++++--- 2 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 openssh-9.6p1-CVE-2023-51385.patch diff --git a/openssh-9.6p1-CVE-2023-51385.patch b/openssh-9.6p1-CVE-2023-51385.patch new file mode 100644 index 0000000..3b83b5c --- /dev/null +++ b/openssh-9.6p1-CVE-2023-51385.patch @@ -0,0 +1,57 @@ +diff --git a/ssh.c b/ssh.c +index 35c48e62..48d93ddf 100644 +--- a/ssh.c ++++ b/ssh.c +@@ -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. */ diff --git a/openssh.spec b/openssh.spec index f54605d..6b54f50 100644 --- a/openssh.spec +++ b/openssh.spec @@ -47,7 +47,7 @@ # Do not forget to bump pam_ssh_agent_auth release if you rewind the main package release to 1 %global openssh_ver 9.0p1 -%global openssh_rel 17 +%global openssh_rel 18 %global pam_ssh_agent_ver 0.10.4 %global pam_ssh_agent_rel 7 @@ -238,10 +238,8 @@ Patch1006: openssh-8.7p1-negotiate-supported-algs.patch # CVE-2023-25136 Patch1007: openssh-8.7p1-CVE-2023-25136.patch -# downstream only -# we skip some ssh-rsa/ssh-dss tests to make native test suite pass -#Patch1100: openssh-8.8p1-skip-some-tests.patch Patch1015: openssh-9.3p1-upstream-cve-2023-38408.patch +Patch1016: openssh-9.6p1-CVE-2023-51385.patch License: BSD Requires: /sbin/nologin @@ -445,8 +443,8 @@ popd %patch1006 -p1 -b .negotiate-supported-algs %patch1007 -p1 -b .cve-2023-25136 -#%patch1100 -p1 -b .skipsshrsadsstests %patch -P 1015 -p1 -b .cve-2023-38408 +%patch -P 1016 -p1 -b .cve-2023-51385 %patch100 -p1 -b .coverity @@ -754,6 +752,10 @@ test -f %{sysconfig_anaconda} && \ %endif %changelog +* Wed Jan 10 2024 Dmitry Belyavskiy - 9.0p1-18 +- Forbid shell metasymbols in username/hostname + Resolves: CVE-2023-51385 + * Wed Jul 26 2023 Mattias Ellert - 9.0p1-17 - Update gssapi-keyex patch for OpenSSH 9.0+ From 8e64184089553e46f2d1d822f69c4a20ec79cc9d Mon Sep 17 00:00:00 2001 From: Dmitry Belyavskiy Date: Wed, 10 Jan 2024 12:07:27 +0100 Subject: [PATCH 11/11] Fix Terrapin attack Resolves: CVE-2023-48795 --- openssh-9.6p1-CVE-2023-48795.patch | 447 +++++++++++++++++++++++++++++ openssh.spec | 4 + 2 files changed, 451 insertions(+) create mode 100644 openssh-9.6p1-CVE-2023-48795.patch diff --git a/openssh-9.6p1-CVE-2023-48795.patch b/openssh-9.6p1-CVE-2023-48795.patch new file mode 100644 index 0000000..03089e1 --- /dev/null +++ b/openssh-9.6p1-CVE-2023-48795.patch @@ -0,0 +1,447 @@ +diff --git a/PROTOCOL b/PROTOCOL +index d453c779..ded935eb 100644 +--- a/PROTOCOL ++++ b/PROTOCOL +@@ -137,6 +137,32 @@ than as a named global or channel request to allow pings with very + 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" +diff --git a/kex.c b/kex.c +index aa5e792d..d478ff6e 100644 +--- a/kex.c ++++ b/kex.c +@@ -65,7 +65,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 * const proposal_names[PROPOSAL_MAX] = { +@@ -177,6 +177,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. +@@ -184,7 +196,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') +@@ -201,10 +213,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); +@@ -466,7 +485,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) +@@ -548,6 +572,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 dispatch_protocol_error(type, seq, ssh); ++ } + for (i = 0; i < ninfo; i++) { + if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0) + return r; +@@ -681,7 +705,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; +@@ -717,7 +741,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) +@@ -981,20 +1005,14 @@ proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX]) + return (1); + } + +-/* returns non-zero if proposal contains any algorithm from algs */ + static int +-has_any_alg(const char *proposal, const char *algs) ++kexalgs_contains(char **peer, const char *ext) + { +- char *cp; +- +- if ((cp = match_list(proposal, algs, NULL)) == NULL) +- return 0; +- free(cp); +- return 1; ++ return has_any_alg(peer[PROPOSAL_KEX_ALGS], ext); + } + + 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; +@@ -1019,13 +1037,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"); ++ } + } + + /* Check whether client supports rsa-sha2 algorithms */ +diff --git a/kex.h b/kex.h +index 5f7ef784..272ebb43 100644 +--- a/kex.h ++++ b/kex.h +@@ -149,6 +149,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 52017def..beb214f9 100644 +--- a/packet.c ++++ b/packet.c +@@ -1207,8 +1207,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; +@@ -1216,6 +1221,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) +@@ -1344,8 +1354,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) +@@ -1629,10 +1615,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; +@@ -1698,6 +1690,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; +@@ -1720,10 +1716,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"); +@@ -1738,19 +1763,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; +@@ -2242,6 +2254,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 || +@@ -2404,6 +2417,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 || +@@ -2732,6 +2746,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/sshconnect2.c b/sshconnect2.c +index df6caf81..0cccbcc4 100644 +--- a/sshconnect2.c ++++ b/sshconnect2.c +@@ -253,7 +253,8 @@ ssh_kex2(struct ssh *ssh, char *host, st + fatal_fr(r, "kex_assemble_namelist"); + free(all_key); + +- if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL) ++ if ((s = kex_names_cat(options.kex_algorithms, ++ "ext-info-c,kex-strict-c-v00@openssh.com")) == NULL) + fatal_f("kex_names_cat"); + myproposal[PROPOSAL_KEX_ALGS] = prop_kex = compat_kex_proposal(ssh, s); + myproposal[PROPOSAL_ENC_ALGS_CTOS] = +@@ -358,7 +358,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 *); +@@ -472,7 +471,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); +@@ -531,12 +530,6 @@ input_userauth_service_accept(int type, u_int32_t seq, struct ssh *ssh) + } + + /* 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) + { +@@ -615,6 +608,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; + } + +diff -up openssh-8.7p1/sshd.c.kexstrict openssh-8.7p1/sshd.c +--- openssh-8.7p1/sshd.c.kexstrict 2023-11-27 13:19:18.855433602 +0100 ++++ openssh-8.7p1/sshd.c 2023-11-27 13:28:10.441325314 +0100 +@@ -2531,10 +2531,14 @@ do_ssh2_kex(struct ssh *ssh) + struct kex *kex; + char *hostkey_types = NULL; + char *prop_kex = NULL, *prop_enc = NULL, *prop_hostkey = NULL; ++ char *cp; + int r; + +- myproposal[PROPOSAL_KEX_ALGS] = prop_kex = compat_kex_proposal(ssh, +- options.kex_algorithms); ++ if ((cp = kex_names_cat(options.kex_algorithms, ++ "kex-strict-s-v00@openssh.com")) == NULL) ++ fatal_f("kex_names_cat"); ++ ++ myproposal[PROPOSAL_KEX_ALGS] = prop_kex = compat_kex_proposal(ssh, cp); + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + myproposal[PROPOSAL_ENC_ALGS_STOC] = prop_enc = + compat_cipher_proposal(ssh, options.ciphers); +@@ -2586,7 +2586,7 @@ do_ssh2_kex(struct ssh *ssh) + if (gss && orig) + xasprintf(&newstr, "%s,%s", gss, orig); + else if (gss) +- newstr = gss; ++ xasprintf(&newstr, "%s,%s", gss, "kex-strict-s-v00@openssh.com"); + else if (orig) + newstr = orig; + +@@ -2650,6 +2654,7 @@ do_ssh2_kex(struct ssh *ssh) + #endif + free(prop_kex); + free(prop_enc); ++ free(cp); + free(prop_hostkey); + debug("KEX done"); + } diff --git a/openssh.spec b/openssh.spec index 6b54f50..f6186e7 100644 --- a/openssh.spec +++ b/openssh.spec @@ -240,6 +240,7 @@ Patch1007: openssh-8.7p1-CVE-2023-25136.patch Patch1015: openssh-9.3p1-upstream-cve-2023-38408.patch Patch1016: openssh-9.6p1-CVE-2023-51385.patch +Patch1017: openssh-9.6p1-CVE-2023-48795.patch License: BSD Requires: /sbin/nologin @@ -445,6 +446,7 @@ popd %patch -P 1015 -p1 -b .cve-2023-38408 %patch -P 1016 -p1 -b .cve-2023-51385 +%patch -P 1017 -p1 -b .cve-2023-48795 %patch100 -p1 -b .coverity @@ -755,6 +757,8 @@ test -f %{sysconfig_anaconda} && \ * Wed Jan 10 2024 Dmitry Belyavskiy - 9.0p1-18 - Forbid shell metasymbols in username/hostname Resolves: CVE-2023-51385 +- Fix Terrapin attack + Resolves: CVE-2023-48795 * Wed Jul 26 2023 Mattias Ellert - 9.0p1-17 - Update gssapi-keyex patch for OpenSSH 9.0+