diff --git a/openssh-10.0-mlkem-nist-fips.patch b/openssh-10.0-mlkem-nist-fips.patch index b610b6e..951e367 100644 --- a/openssh-10.0-mlkem-nist-fips.patch +++ b/openssh-10.0-mlkem-nist-fips.patch @@ -1,17 +1,85 @@ +diff --git a/compat.c b/compat.c +index 4e611dc39..b5f4cc685 100644 +--- a/compat.c ++++ b/compat.c +@@ -36,6 +36,7 @@ + #include "compat.h" + #include "log.h" + #include "match.h" ++#include + + /* determine bug flags from SSH protocol banner */ + void +@@ -148,8 +149,9 @@ char * + compat_kex_proposal(struct ssh *ssh, const char *p) + { + char *cp = NULL, *cp2 = NULL; ++ int mlkem_available = is_mlkem768_available(); + +- if ((ssh->compat & (SSH_BUG_CURVE25519PAD|SSH_OLD_DHGEX)) == 0) ++ if ((ssh->compat & (SSH_BUG_CURVE25519PAD|SSH_OLD_DHGEX)) == 0 && mlkem_available == 2) + return xstrdup(p); + debug2_f("original KEX proposal: %s", p); + if ((ssh->compat & SSH_BUG_CURVE25519PAD) != 0) +@@ -164,6 +166,25 @@ compat_kex_proposal(struct ssh *ssh, const char *p) + free(cp); + cp = cp2; + } ++ if (mlkem_available == 2) ++ return cp ? cp : xstrdup(p); ++ if (mlkem_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 (mlkem_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 --git a/compat.h b/compat.h +index 2e6db5bf9..b78e55b69 100644 +--- a/compat.h ++++ b/compat.h +@@ -62,4 +62,5 @@ struct ssh; + + void compat_banner(struct ssh *, const char *); + char *compat_kex_proposal(struct ssh *, const char *); ++int is_mlkem768_available(void); + #endif diff --git a/kex-names.c b/kex-names.c index a728e0b38..9b852e8ab 100644 --- a/kex-names.c +++ b/kex-names.c -@@ -112,13 +112,30 @@ static const struct kexalg gss_kexalgs[] = { +@@ -35,6 +35,7 @@ + #include + #include + #include ++#include + #endif + + #include "kex.h" +@@ -113,14 +113,27 @@ static const struct kexalg gss_kexalgs[] { NULL, 0, -1, -1}, }; +/* + * 0 - unavailable + * 1 - available in non-FIPS mode -+ * 2 - available in FIPS mode ++ * 2 - available always + */ - static int is_mlkem768_available() +-static int is_mlkem768_available() ++int is_mlkem768_available() { static int is_fetched = -1; @@ -20,21 +88,18 @@ index a728e0b38..9b852e8ab 100644 - is_fetched = mlkem768 != NULL ? 1 : 0; + EVP_KEM *mlkem768 = NULL; + -+ if (FIPS_mode() == 1) { -+ mlkem768 = EVP_KEM_fetch(NULL, "mlkem768", NULL); -+ is_fetched = mlkem768 != NULL ? 2 : 0; -+ -+ if (is_fetched == 0) { -+ mlkem768 = EVP_KEM_fetch(NULL, "mlkem768", "provider=default,-fips"); -+ is_fetched = mlkem768 != NULL ? 1 : 0; -+ } -+ } else { -+ mlkem768 = EVP_KEM_fetch(NULL, "mlkem768", NULL); -+ is_fetched = mlkem768 != NULL ? 1 : 0; ++ 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; @@ -131,13 +148,32 @@ kex_alg_list_internal(char sep, const struct kexalg *algs) char *ret = NULL, *tmp; size_t nlen, rlen = 0; @@ -108,6 +173,25 @@ index a728e0b38..9b852e8ab 100644 for (k = kexalgs; k->name != NULL; k++) { if (strcmp(k->name, name) == 0) +@@ -292,8 +295,16 @@ 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) ++ || (strcmp(p, KEX_MLKEM768NISTP256_SHA256) == 0) ++ || (strcmp(p, KEX_MLKEM1024NISTP384_SHA384) == 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/kexgen.c b/kexgen.c index 6e910f84f..4bc3f9faf 100644 --- a/kexgen.c diff --git a/openssh.spec b/openssh.spec index f907eaf..6a89813 100644 --- a/openssh.spec +++ b/openssh.spec @@ -43,7 +43,7 @@ Summary: An open source implementation of SSH protocol version 2 Name: openssh Version: %{openssh_ver} -Release: 19%{?dist}.alma.1 +Release: 20%{?dist}.alma.1 URL: http://www.openssh.com/portable.html Source0: ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz Source1: ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz.asc @@ -707,9 +707,13 @@ test -f %{sysconfig_anaconda} && \ %attr(0755,root,root) %{_libdir}/sshtest/sk-dummy.so %changelog -* Sat Dec 13 2025 Koichiro Iwao - 9.9p1-19.alma.1 +* Sat Feb 28 2026 Koichiro Iwao - 9.9p1-20.alma.1 - Unpatch Red Hat help message +* Wed Feb 25 2026 Dmitry Belyavskiy - 9.9p1-20 +- Provide a way to skip unsupported ML-KEM hybrid algorithms in FIPS mode + Resolves: RHEL-151579 + * Thu Dec 11 2025 Dmitry Belyavskiy - 9.9p1-19 - Support of hybrid MLKEM key exchange methods in FIPS mode Resolves: RHEL-125929