diff --git a/openssh-9.9p1-compat-mlkem.patch b/openssh-9.9p1-compat-mlkem.patch new file mode 100644 index 0000000..bc2393c --- /dev/null +++ b/openssh-9.9p1-compat-mlkem.patch @@ -0,0 +1,101 @@ +diff -up openssh-9.9p1/compat.c.xxx openssh-9.9p1/compat.c +--- openssh-9.9p1/compat.c.xxx 2026-02-25 12:58:14.083760269 +0100 ++++ openssh-9.9p1/compat.c 2026-02-25 13:28:21.154300255 +0100 +@@ -36,6 +36,9 @@ + #include "compat.h" + #include "log.h" + #include "match.h" ++#include ++#include ++#include + + /* determine bug flags from SSH protocol banner */ + void +@@ -143,13 +145,40 @@ compat_banner(struct ssh *ssh, const cha + ssh->compat |= SSH_RH_RSASIGSHA; + } + ++/* ++ * 0 - unavailable ++ * 1 - available in non-FIPS mode ++ * 2 - available always ++ */ ++static int is_mlkem768_available() ++{ ++ static int is_fetched = -1; ++ ++ if (is_fetched == -1) { ++ EVP_KEM *mlkem768 = NULL; ++ ++ ERR_set_mark(); ++ mlkem768 = EVP_KEM_fetch(NULL, "mlkem768", NULL); ++ is_fetched = (mlkem768 == NULL) ? 0 : 2; ++ if (is_fetched == 0 && FIPS_mode() == 1) { ++ mlkem768 = EVP_KEM_fetch(NULL, "mlkem768", "provider=default,-fips"); ++ is_fetched = (mlkem768 == NULL) ? 0 : 1; ++ } ++ EVP_KEM_free(mlkem768); ++ ERR_pop_to_mark(); ++ } ++ ++ return is_fetched; ++} ++ + /* Always returns pointer to allocated memory, caller must free. */ + char * + compat_kex_proposal(struct ssh *ssh, const char *p) + { + char *cp = NULL, *cp2 = NULL; ++ int ml_kem_available = is_mlkem768_available(); + +- if ((ssh->compat & (SSH_BUG_CURVE25519PAD|SSH_OLD_DHGEX)) == 0) ++ if ((ssh->compat & (SSH_BUG_CURVE25519PAD|SSH_OLD_DHGEX)) == 0 && is_mlkem768_available() == 2) + return xstrdup(p); + debug2_f("original KEX proposal: %s", p); + if ((ssh->compat & SSH_BUG_CURVE25519PAD) != 0) +@@ -164,6 +199,25 @@ compat_kex_proposal(struct ssh *ssh, con + free(cp); + cp = cp2; + } ++ if (ml_kem_available == 2) ++ return cp ? cp : xstrdup(p); ++ if (ml_kem_available == 1 && FIPS_mode()) { ++ if ((cp2 = match_filter_denylist(cp ? cp : p, ++ "mlkem768x25519-sha256")) == NULL) ++ fatal("match_filter_denylist failed"); ++ free(cp); ++ cp = cp2; ++ } ++ if (ml_kem_available == 0) { ++ if ((cp2 = match_filter_denylist(cp ? cp : p, ++ "mlkem768x25519-sha256," ++ "mlkem768nistp256-sha256," ++ "mlkem1024nistp384-sha384")) == NULL) ++ fatal("match_filter_denylist failed"); ++ free(cp); ++ cp = cp2; ++ } ++ + if (cp == NULL || *cp == '\0') + fatal("No supported key exchange algorithms found"); + debug2_f("compat KEX proposal: %s", cp); +diff -up openssh-9.9p1/kex-names.c.xxx openssh-9.9p1/kex-names.c +--- openssh-9.9p1/kex-names.c.xxx 2026-02-25 14:52:59.802597974 +0100 ++++ openssh-9.9p1/kex-names.c 2026-02-25 15:31:13.376020525 +0100 +@@ -291,8 +291,14 @@ kex_names_valid(const char *names) + for ((p = strsep(&cp, ",")); p && *p != '\0'; + (p = strsep(&cp, ","))) { + if (kex_alg_by_name(p) == NULL) { +- if (FIPS_mode()) +- error("\"%.100s\" is not allowed in FIPS mode", p); ++ if (FIPS_mode()) { ++ if ((strcmp(p, KEX_MLKEM768X25519_SHA256) == 0)) { ++ debug("\"%.100s\" is not allowed in FIPS mode", p); ++ continue; ++ } ++ else ++ error("\"%.100s\" is not allowed in FIPS mode", p); ++ } + else + error("Unsupported KEX algorithm \"%.100s\"", p); + free(s); diff --git a/openssh.spec b/openssh.spec index 97563c9..5477a86 100644 --- a/openssh.spec +++ b/openssh.spec @@ -47,9 +47,9 @@ # Do not forget to bump pam_ssh_agent_auth release if you rewind the main package release to 1 %global openssh_ver 9.9p1 -%global openssh_rel 3 +%global openssh_rel 4 %global pam_ssh_agent_ver 0.10.4 -%global pam_ssh_agent_rel 6 +%global pam_ssh_agent_rel 7 Summary: An open source implementation of SSH protocol version 2 Name: openssh @@ -244,6 +244,7 @@ Patch1036: openssh-9.9p1-canonical-match-user.patch Patch1037: openssh-9.9p1-reject-cntrl-chars-in-username.patch # upstream 43b3bff47bb029f2299bacb6a36057981b39fdb0 Patch1038: openssh-9.9p1-reject-null-char-in-url-string.patch +Patch1039: openssh-9.9p1-compat-mlkem.patch License: BSD Requires: /sbin/nologin @@ -458,6 +459,7 @@ popd %patch1036 -p1 -b .canonical-match-user %patch1037 -p1 -b .reject-cntrl-chars-in-username %patch1038 -p1 -b .reject-null-char-in-url-string +%patch1039 -p1 -b .skip-mlkem-when-na autoreconf pushd pam_ssh_agent_auth-pam_ssh_agent_auth-%{pam_ssh_agent_ver} @@ -746,6 +748,10 @@ test -f %{sysconfig_anaconda} && \ %endif %changelog +* Wed Feb 25 2026 Dmitry Belyavskiy - 9.9p1-4 +- Provide a way to skip unsupported ML-KEM hybrid algorithms in FIPS mode + Resolves: RHEL-151580 + * Tue Dec 09 2025 Zoltan Fridrich - 9.9p1-3 - Enable support for DSA keys Resolves: RHEL-127624