Various fixes

- pam_unix: always run the helper to obtain shadow password file
  entries.
- pam_access: always match local address and clarify LOCAL keyword
  behaviour.

Resolves: CVE-2024-10041. RHEL-62879
Resolves: RHEL-23631 and RHEL-39943

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
This commit is contained in:
Iker Pedrosa 2024-11-04 11:53:58 +01:00
parent 76980b6399
commit 82c088fbad
3 changed files with 247 additions and 1 deletions

View File

@ -0,0 +1,119 @@
From ecaaf4456e5aeacae1acdb1775bb5aadd3b19e13 Mon Sep 17 00:00:00 2001
From: Iker Pedrosa <ipedrosa@redhat.com>
Date: Wed, 16 Oct 2024 12:41:09 +0200
Subject: [PATCH 1/2] pam_access: always match local address
* modules/pam_access/pam_access.c: match the local address regardless of
the IP version in use.
In some circumstances the `localhost` may be translated to IPv4 or IPv6,
but the configuration file only indicated the address for one of the two
versions. Since the originating value is set in `PAM_RHOST` and PAM has
no control over it, let's match the local addresses regardless of the IP
version in use.
Resolves: https://issues.redhat.com/browse/RHEL-23018
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
---
modules/pam_access/pam_access.c | 30 ++++++++++++++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c
index bfbc6d57..48e7c7e9 100644
--- a/modules/pam_access/pam_access.c
+++ b/modules/pam_access/pam_access.c
@@ -306,6 +306,23 @@ isipaddr (const char *string, int *addr_type,
return is_ip;
}
+/* is_local_addr - checks if the IP address is local */
+static int
+is_local_addr (const char *string, int addr_type)
+{
+ if (addr_type == AF_INET) {
+ if (strcmp(string, "127.0.0.1") == 0) {
+ return YES;
+ }
+ } else if (addr_type == AF_INET6) {
+ if (strcmp(string, "::1") == 0) {
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
/* are_addresses_equal - translate IP address strings to real IP
* addresses and compare them to find out if they are equal.
@@ -327,9 +344,18 @@ are_addresses_equal (const char *ipaddr0, const char *ipaddr1,
if (isipaddr (ipaddr1, &addr_type1, &addr1) == NO)
return NO;
- if (addr_type0 != addr_type1)
- /* different address types */
+ if (addr_type0 != addr_type1) {
+ /* different address types, but there is still a possibility that they are
+ * both local addresses
+ */
+ int local1 = is_local_addr(ipaddr0, addr_type0);
+ int local2 = is_local_addr(ipaddr1, addr_type1);
+
+ if (local1 == YES && local2 == YES)
+ return YES;
+
return NO;
+ }
if (netmask != NULL) {
/* Got a netmask, so normalize addresses? */
--
2.47.0
From 641dfd1084508c63f3590e93a35b80ffc50774e5 Mon Sep 17 00:00:00 2001
From: Iker Pedrosa <ipedrosa@redhat.com>
Date: Fri, 18 Oct 2024 10:27:07 +0200
Subject: [PATCH 2/2] pam_access: clarify `LOCAL` keyword behaviour
* modules/pam_access/access.conf.5.xml: `LOCAL` keyword behaviour
explanation was focused on the development internals. Let's clarify it
by rephrasing it to something a sysadmin can understand.
Resolves: https://issues.redhat.com/browse/RHEL-39943
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
---
modules/pam_access/access.conf.5.xml | 17 ++++++-----------
1 file changed, 6 insertions(+), 11 deletions(-)
diff --git a/modules/pam_access/access.conf.5.xml b/modules/pam_access/access.conf.5.xml
index 35a1a8fe..0b93db00 100644
--- a/modules/pam_access/access.conf.5.xml
+++ b/modules/pam_access/access.conf.5.xml
@@ -79,17 +79,12 @@
with network mask (where network mask can be a decimal number or an
internet address also), <emphasis>ALL</emphasis> (which always matches)
or <emphasis>LOCAL</emphasis>. The <emphasis>LOCAL</emphasis>
- keyword matches if and only if
- <citerefentry><refentrytitle>pam_get_item</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- when called with an <parameter>item_type</parameter> of
- <emphasis>PAM_RHOST</emphasis>, returns <code>NULL</code> or an
- empty string (and therefore the
- <replaceable>origins</replaceable> field is compared against the
- return value of
- <citerefentry><refentrytitle>pam_get_item</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- called with an <parameter>item_type</parameter> of
- <emphasis>PAM_TTY</emphasis> or, absent that,
- <emphasis>PAM_SERVICE</emphasis>).
+ keyword matches when the user connects without a network
+ connection (e.g., <emphasis>su</emphasis>,
+ <emphasis>login</emphasis>). A connection through the loopback
+ device (e.g., <command>ssh user@localhost</command>) is
+ considered a network connection, and thus, the
+ <emphasis>LOCAL</emphasis> keyword does not match.
</para>
<para>
--
2.47.0

View File

@ -0,0 +1,114 @@
diff -up Linux-PAM-1.5.1/modules/pam_unix/passverify.c.fail1 Linux-PAM-1.5.1/modules/pam_unix/passverify.c
--- Linux-PAM-1.5.1/modules/pam_unix/passverify.c.fail1 2024-11-04 11:42:51.962791265 +0100
+++ Linux-PAM-1.5.1/modules/pam_unix/passverify.c 2024-11-04 11:45:18.246218579 +0100
@@ -239,17 +239,21 @@ PAMH_ARG_DECL(int get_account_info,
return PAM_UNIX_RUN_HELPER;
#endif
} else if (is_pwd_shadowed(*pwd)) {
+#ifdef HELPER_COMPILE
/*
- * ...and shadow password file entry for this user,
+ * shadow password file entry for this user,
* if shadowing is enabled
*/
-#ifndef HELPER_COMPILE
- if (geteuid() || SELINUX_ENABLED)
- return PAM_UNIX_RUN_HELPER;
-#endif
- *spwdent = pam_modutil_getspnam(pamh, name);
+ *spwdent = getspnam(name);
if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL)
return PAM_AUTHINFO_UNAVAIL;
+#else
+ /*
+ * The helper has to be invoked to deal with
+ * the shadow password file entry.
+ */
+ return PAM_UNIX_RUN_HELPER;
+#endif
}
} else {
return PAM_USER_UNKNOWN;
From 8d0c575336ad301cd14e16ad2fdec6fe621764b8 Mon Sep 17 00:00:00 2001
From: Sergei Trofimovich <slyich@gmail.com>
Date: Thu, 28 Mar 2024 21:58:35 +0000
Subject: [PATCH] pam_unix: allow empty passwords with non-empty hashes
Before the change pam_unix has different behaviours for a user with
empty password for these two `/etc/shadow` entries:
nulloktest:$6$Yy4ty2jJ$bsVQWo8qlXC6UHq1/qTC3UR60ZJKmKApJ3Wj7DreAy8FxlVKtlDnplFQ7jMLVlDqordE7e4t49GvTb.aI59TP0:1::::::
nulloktest::1::::::
The entry with a hash was rejected and the entry without was accepted.
The rejection happened because 9e74e90147c "pam_unix: avoid determining
if user exists" introduced the following rejection check (slightly
simplified):
...
} else if (p[0] == '\0' && nullok) {
if (hash[0] != '\0') {
retval = PAM_AUTH_ERR;
}
We should not reject the user with a hash assuming it's non-empty.
The change does that by pushing empty password check into
`verify_pwd_hash()`.
`NixOS` generates such hashed entries for empty passwords as if they
were non-empty using the following perl code:
sub hashPassword {
my ($password) = @_;
my $salt = "";
my @chars = ('.', '/', 0..9, 'A'..'Z', 'a'..'z');
$salt .= $chars[rand 64] for (1..8);
return crypt($password, '$6$' . $salt . '$');
}
Resolves: https://github.com/linux-pam/linux-pam/issues/758
Fixes: 9e74e90147c "pam_unix: avoid determining if user exists"
Signed-off-by: Sergei Trofimovich <slyich@gmail.com>
---
modules/pam_unix/passverify.c | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/modules/pam_unix/passverify.c b/modules/pam_unix/passverify.c
index 30045333..1c83f1aa 100644
--- a/modules/pam_unix/passverify.c
+++ b/modules/pam_unix/passverify.c
@@ -76,9 +76,13 @@ PAMH_ARG_DECL(int verify_pwd_hash,
strip_hpux_aging(hash);
hash_len = strlen(hash);
- if (!hash_len) {
+
+ if (p && p[0] == '\0' && !nullok) {
+ /* The passed password is empty */
+ retval = PAM_AUTH_ERR;
+ } else if (!hash_len) {
/* the stored password is NULL */
- if (nullok) { /* this means we've succeeded */
+ if (p && p[0] == '\0' && nullok) { /* this means we've succeeded */
D(("user has empty password - access granted"));
retval = PAM_SUCCESS;
} else {
@@ -1109,12 +1113,6 @@ helper_verify_password(const char *name, const char *p, int nullok)
if (pwd == NULL || hash == NULL) {
helper_log_err(LOG_NOTICE, "check pass; user unknown");
retval = PAM_USER_UNKNOWN;
- } else if (p[0] == '\0' && nullok) {
- if (hash[0] == '\0') {
- retval = PAM_SUCCESS;
- } else {
- retval = PAM_AUTH_ERR;
- }
} else {
retval = verify_pwd_hash(p, hash, nullok);
}
--
2.47.0

View File

@ -3,7 +3,7 @@
Summary: An extensible library which provides authentication for applications
Name: pam
Version: 1.5.1
Release: 20%{?dist}
Release: 21%{?dist}
# The library is BSD licensed with option to relicense as GPLv2+
# - this option is redundant as the BSD license allows that anyway.
# pam_timestamp, pam_loginuid, and pam_console modules are GPLv2+.
@ -67,6 +67,11 @@ Patch19: pam-1.5.1-access-handle-hostnames.patch
Patch20: pam-1.5.1-namespace-protect-dir.patch
# https://github.com/linux-pam/linux-pam/commit/ec1fb9ddc6c252d8c61379e9385ca19c036fcb96
Patch21: pam-1.5.1-libpam-support-long-lines.patch
# https://github.com/linux-pam/linux-pam/commit/b3020da7da384d769f27a8713257fbe1001878be
# https://github.com/linux-pam/linux-pam/commit/8d0c575336ad301cd14e16ad2fdec6fe621764b8
Patch22: pam-1.5.1-pam-unix-shadow-password.patch
# https://github.com/linux-pam/linux-pam/commit/641dfd1084508c63f3590e93a35b80ffc50774e5
Patch23: pam-1.5.1-pam-access-local.patch
%global _pamlibdir %{_libdir}
%global _moduledir %{_libdir}/security
@ -170,6 +175,8 @@ cp %{SOURCE18} .
%patch19 -p1 -b .access-handle-hostnames
%patch20 -p1 -b .namespace-protect-dir
%patch21 -p1 -b .libpam-support-long-lines
%patch22 -p1 -b .pam-unix-shadow-password
%patch23 -p1 -b .pam-access-local
autoreconf -i
@ -425,6 +432,12 @@ done
%doc doc/sag/*.txt doc/sag/html
%changelog
* Mon Nov 4 2024 Iker Pedrosa <ipedrosa@redhat.com> - 1.5.1-21
- pam_unix: always run the helper to obtain shadow password file entries.
CVE-2024-10041. Resolves: RHEL-62879
- pam_access: always match local address and clarify LOCAL keyword behaviour.
Resolves: RHEL-23631 and RHEL-39943
* Tue Jun 18 2024 Iker Pedrosa <ipedrosa@redhat.com> - 1.5.1-20
- libpam: support long lines in service files. Resolves: RHEL-40705