From 4d79519f12054d58f5c0db1bfaa577c90a66e1c8 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Thu, 9 Apr 2026 11:54:12 +0200 Subject: [PATCH] Various fixes - pam_access: support UID and GID in access.conf - pam_lastlog: fix file locking Resolves: RHEL-119868 Resolves: RHEL-118522 Signed-off-by: Iker Pedrosa --- ...1.5.1-pam-access-uid-gid-access-conf.patch | 176 ++++++++++++++++++ pam-1.5.1-pam-lastlog-file-locking.patch | 101 ++++++++++ pam.spec | 14 +- 3 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 pam-1.5.1-pam-access-uid-gid-access-conf.patch create mode 100644 pam-1.5.1-pam-lastlog-file-locking.patch diff --git a/pam-1.5.1-pam-access-uid-gid-access-conf.patch b/pam-1.5.1-pam-access-uid-gid-access-conf.patch new file mode 100644 index 0000000..6fc5d19 --- /dev/null +++ b/pam-1.5.1-pam-access-uid-gid-access-conf.patch @@ -0,0 +1,176 @@ +diff -up Linux-PAM-1.5.1/libpam/include/pam_inline.h.pam-access-uid-gid-access-conf Linux-PAM-1.5.1/libpam/include/pam_inline.h +--- Linux-PAM-1.5.1/libpam/include/pam_inline.h.pam-access-uid-gid-access-conf 2026-04-07 16:43:45.979027569 +0200 ++++ Linux-PAM-1.5.1/libpam/include/pam_inline.h 2026-04-07 16:43:45.984864383 +0200 +@@ -41,6 +41,26 @@ + #define PAM_ARRAY_SIZE(a_) (sizeof(a_) / sizeof((a_)[0]) + PAM_MUST_BE_ARRAY(a_)) + + /* ++ * Zero-extend a signed integer type to unsigned long long. ++ */ ++# define zero_extend_signed_to_ull(v_) \ ++ (sizeof(v_) == sizeof(char) ? (unsigned long long) (unsigned char) (v_) : \ ++ sizeof(v_) == sizeof(short) ? (unsigned long long) (unsigned short) (v_) : \ ++ sizeof(v_) == sizeof(int) ? (unsigned long long) (unsigned int) (v_) : \ ++ sizeof(v_) == sizeof(long) ? (unsigned long long) (unsigned long) (v_) : \ ++ (unsigned long long) (v_)) ++ ++/* ++ * Sign-extend an unsigned integer type to long long. ++ */ ++# define sign_extend_unsigned_to_ll(v_) \ ++ (sizeof(v_) == sizeof(char) ? (long long) (signed char) (v_) : \ ++ sizeof(v_) == sizeof(short) ? (long long) (signed short) (v_) : \ ++ sizeof(v_) == sizeof(int) ? (long long) (signed int) (v_) : \ ++ sizeof(v_) == sizeof(long) ? (long long) (signed long) (v_) : \ ++ (long long) (v_)) ++ ++/* + * Returns NULL if STR does not start with PREFIX, + * or a pointer to the first char in STR after PREFIX. + * The length of PREFIX is specified by PREFIX_LEN. +diff -up Linux-PAM-1.5.1/modules/pam_access/access.conf.5.xml.pam-access-uid-gid-access-conf Linux-PAM-1.5.1/modules/pam_access/access.conf.5.xml +--- Linux-PAM-1.5.1/modules/pam_access/access.conf.5.xml.pam-access-uid-gid-access-conf 2026-04-07 16:43:45.976429463 +0200 ++++ Linux-PAM-1.5.1/modules/pam_access/access.conf.5.xml 2026-04-07 16:43:45.984973066 +0200 +@@ -67,10 +67,10 @@ + + The second field, the + users/group +- field, should be a list of one or more login names, group names, or ++ field, should be a list of one or more login names, group names, uid, gid, or + ALL (which always matches). To differentiate + user entries from group entries, group entries should be written +- with brackets, e.g. (group). ++ with brackets, e.g. (group) or (gid). + + + +@@ -175,6 +175,12 @@ + -:root:ALL + + ++ A user with uid 1003 and a group with gid ++ 1000 should be allowed to get access ++ from all other sources. ++ ++ +:(1000) 1003:ALL ++ + User foo and members of netgroup + admins should be allowed to get access + from all sources. This will only work if netgroup service is available. +diff -up Linux-PAM-1.5.1/modules/pam_access/pam_access.c.pam-access-uid-gid-access-conf Linux-PAM-1.5.1/modules/pam_access/pam_access.c +--- Linux-PAM-1.5.1/modules/pam_access/pam_access.c.pam-access-uid-gid-access-conf 2026-04-07 16:43:45.976751995 +0200 ++++ Linux-PAM-1.5.1/modules/pam_access/pam_access.c 2026-04-07 16:48:45.995605657 +0200 +@@ -254,7 +254,7 @@ typedef int match_func (pam_handle_t *, char *, struct login_info *); + static int list_match (pam_handle_t *, char *, char *, struct login_info *, + match_func *); + static int user_match (pam_handle_t *, char *, struct login_info *); +-static int group_match (pam_handle_t *, const char *, const char *, int); ++static int group_match (pam_handle_t *, char *, const char *, int); + static int from_match (pam_handle_t *, char *, struct login_info *); + static int remote_match (pam_handle_t *, char *, struct login_info *); + static int string_match (pam_handle_t *, const char *, const char *, int); +@@ -527,7 +527,30 @@ netgroup_match (pam_handle_t *pamh, cons + return retval; + } + +-/* user_match - match a username against one token */ ++/* user_name_or_uid_match - match a username or user uid against one token */ ++static int ++user_name_or_uid_match(pam_handle_t *pamh, const char *tok, ++ const struct login_info *item) ++{ ++ /* ALL or exact match of username */ ++ int rv = string_match(pamh, tok, item->user->pw_name, item->debug); ++ if (rv != NO) ++ return rv; ++ ++ if (tok[strspn(tok, "0123456789")] != '\0') ++ return NO; ++ ++ char buf[sizeof(long long) * 3 + 1]; ++ snprintf(buf, sizeof(buf), "%llu", ++ zero_extend_signed_to_ull(item->user->pw_uid)); ++ if (item->debug) ++ pam_syslog(pamh, LOG_DEBUG, "user_match: tok=%s, uid=%s", tok, buf); ++ ++ /* check for exact match of uid */ ++ return string_match (pamh, tok, buf, item->debug); ++} ++ ++/* user_match - match a user against one token */ + + static int + user_match (pam_handle_t *pamh, char *tok, struct login_info *item) +@@ -578,7 +601,7 @@ user_match (pam_handle_t *pamh, char *to + hostname = item->hostname; + } + return (netgroup_match (pamh, tok + 1, hostname, string, item->debug)); +- } else if ((rv=string_match (pamh, tok, string, item->debug)) != NO) /* ALL or exact match */ ++ } else if ((rv=user_name_or_uid_match(pamh, tok, item)) != NO) /* ALL or exact match */ + return rv; + else if (item->only_new_group_syntax == NO && + pam_modutil_user_in_group_nam_nam (pamh, +@@ -590,14 +613,41 @@ user_match (pam_handle_t *pamh, char *to + } + + ++/* group_name_or_gid_match - match a group name or group gid against one token */ ++static int ++group_name_or_gid_match(pam_handle_t *pamh, const char *tok, ++ const char *usr, int debug) ++{ ++ /* check for exact match of group name */ ++ if (pam_modutil_user_in_group_nam_nam(pamh, usr, tok) != NO) ++ return YES; ++ ++ if (tok[strspn(tok, "0123456789")] != '\0') ++ return NO; ++ ++ char *endptr = NULL; ++ errno = 0; ++ unsigned long int ul = strtoul(tok, &endptr, 10); ++ gid_t gid = (gid_t) ul; ++ if (errno != 0 ++ || tok == endptr ++ || *endptr != '\0' ++ || (unsigned long) zero_extend_signed_to_ull(gid) != ul) { ++ return NO; ++ } ++ ++ if (debug) ++ pam_syslog(pamh, LOG_DEBUG, "group_match: user=%s, gid=%s", usr, tok); ++ ++ /* check for exact match of gid */ ++ return pam_modutil_user_in_group_nam_gid(pamh, usr, gid); ++} ++ + /* group_match - match a username against token named group */ + + static int +-group_match (pam_handle_t *pamh, const char *tok, const char* usr, +- int debug) ++group_match (pam_handle_t *pamh, char *tok, const char* usr, int debug) + { +- char grptok[BUFSIZ]; +- + if (debug) + pam_syslog (pamh, LOG_DEBUG, + "group_match: grp=%s, user=%s", tok, usr); +@@ -606,13 +656,13 @@ group_match (pam_handle_t *pamh, const c + return NO; + + /* token is received under the format '(...)' */ +- memset(grptok, 0, BUFSIZ); +- strncpy(grptok, tok + 1, strlen(tok) - 2); ++ tok++; ++ tok[strlen(tok) - 1] = '\0'; + +- if (pam_modutil_user_in_group_nam_nam(pamh, usr, grptok)) ++ if (group_name_or_gid_match (pamh, tok, usr, debug)) + return YES; + +- return NO; ++ return NO; + } + + diff --git a/pam-1.5.1-pam-lastlog-file-locking.patch b/pam-1.5.1-pam-lastlog-file-locking.patch new file mode 100644 index 0000000..0f69915 --- /dev/null +++ b/pam-1.5.1-pam-lastlog-file-locking.patch @@ -0,0 +1,101 @@ +From 2e7910e3be93d99e865d9b86c38a1b56e4a95d6e Mon Sep 17 00:00:00 2001 +From: Davin Shearer <2205472+scholarsmate@users.noreply.github.com> +Date: Thu, 28 Apr 2022 04:18:24 -0400 +Subject: [PATCH] pam_lastlog: fix file locking + +Fixed 2 instances in the pam_lastlog module where file locks were +not being enforced when reading and writing last login records. + +* modules/pam_lastlog/pam_lastlog.c (last_login_write): The write lock +failure is fatal after 3 tries. +(last_login_read): The read lock failure is non-fatal after 3 tries. +It is non-fatal in the read case due to concerns about a possible DoS. +--- + modules/pam_lastlog/pam_lastlog.c | 36 ++++++++++++++++++++++--------- + 1 file changed, 26 insertions(+), 10 deletions(-) + +diff --git a/modules/pam_lastlog/pam_lastlog.c b/modules/pam_lastlog/pam_lastlog.c +index 121e7560..797a61ce 100644 +--- a/modules/pam_lastlog/pam_lastlog.c ++++ b/modules/pam_lastlog/pam_lastlog.c +@@ -57,14 +57,13 @@ struct lastlog { + # define PATH_LOGIN_DEFS "/etc/login.defs" + #endif + +-/* XXX - time before ignoring lock. Is 1 sec enough? */ +-#define LASTLOG_IGNORE_LOCK_TIME 1 +- + #define DEFAULT_HOST "" /* "[no.where]" */ + #define DEFAULT_TERM "" /* "tt???" */ + + #define DEFAULT_INACTIVE_DAYS 90 + #define MAX_INACTIVE_DAYS 100000 ++#define LOCK_RETRIES 3 /* number of file lock retries */ ++#define LOCK_RETRY_DELAY 1 /* seconds to wait between lock attempts */ + + #include + #include +@@ -266,6 +265,7 @@ last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid, time_t + { + struct flock last_lock; + struct lastlog last_login; ++ int lock_retries = LOCK_RETRIES; + int retval = PAM_SUCCESS; + char the_time[256]; + char *date = NULL; +@@ -278,11 +278,19 @@ last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid, time_t + last_lock.l_start = sizeof(last_login) * (off_t) uid; + last_lock.l_len = sizeof(last_login); + +- if (fcntl(last_fd, F_SETLK, &last_lock) < 0) { ++ while (fcntl(last_fd, F_SETLK, &last_lock) < 0) { ++ if (0 == --lock_retries) { ++ /* read lock failed, proceed anyway to avoid possible DoS */ ++ D(("locking %s failed", _PATH_LASTLOG)); ++ pam_syslog(pamh, LOG_INFO, ++ "file %s is locked/read, proceeding anyway", ++ _PATH_LASTLOG); ++ break; ++ } + D(("locking %s failed..(waiting a little)", _PATH_LASTLOG)); +- pam_syslog(pamh, LOG_WARNING, +- "file %s is locked/read", _PATH_LASTLOG); +- sleep(LASTLOG_IGNORE_LOCK_TIME); ++ pam_syslog(pamh, LOG_INFO, ++ "file %s is locked/read, retrying", _PATH_LASTLOG); ++ sleep(LOCK_RETRY_DELAY); + } + + if (pam_modutil_read(last_fd, (char *) &last_login, +@@ -380,6 +388,7 @@ last_login_write(pam_handle_t *pamh, int announce, int last_fd, + int setrlimit_res; + struct flock last_lock; + struct lastlog last_login; ++ int lock_retries = LOCK_RETRIES; + time_t ll_time; + const void *void_remote_host = NULL; + const char *remote_host; +@@ -426,10 +435,17 @@ last_login_write(pam_handle_t *pamh, int announce, int last_fd, + last_lock.l_start = sizeof(last_login) * (off_t) uid; + last_lock.l_len = sizeof(last_login); + +- if (fcntl(last_fd, F_SETLK, &last_lock) < 0) { ++ while (fcntl(last_fd, F_SETLK, &last_lock) < 0) { ++ if (0 == --lock_retries) { ++ D(("locking %s failed", _PATH_LASTLOG)); ++ pam_syslog(pamh, LOG_ERR, ++ "file %s is locked/write", _PATH_LASTLOG); ++ return PAM_SERVICE_ERR; ++ } + D(("locking %s failed..(waiting a little)", _PATH_LASTLOG)); +- pam_syslog(pamh, LOG_WARNING, "file %s is locked/write", _PATH_LASTLOG); +- sleep(LASTLOG_IGNORE_LOCK_TIME); ++ pam_syslog(pamh, LOG_INFO, ++ "file %s is locked/write, retrying", _PATH_LASTLOG); ++ sleep(LOCK_RETRY_DELAY); + } + + /* +-- +2.53.0 + diff --git a/pam.spec b/pam.spec index 1d07632..337b781 100644 --- a/pam.spec +++ b/pam.spec @@ -3,7 +3,7 @@ Summary: An extensible library which provides authentication for applications Name: pam Version: 1.5.1 -Release: 28%{?dist} +Release: 29%{?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+. @@ -85,6 +85,11 @@ Patch26: pam-1.5.1-pam-inline-pam-asprintf.patch Patch27: pam-1.5.1-pam-namespace-rebase.patch # https://github.com/linux-pam/linux-pam/commit/7d96d452e65ba5dec73f2c77104113977dd3aeb1 Patch28: pam-1.5.1-pam-faillock-skip.patch +# https://github.com/linux-pam/linux-pam/commit/83c344ee5a5eb4796e435bce897b83cae3465243 +# https://github.com/linux-pam/linux-pam/commit/fc927d8f1a6d81e5bcf58096871684b35b793fe2 +Patch29: pam-1.5.1-pam-access-uid-gid-access-conf.patch +# https://github.com/linux-pam/linux-pam/commit/2e7910e3be93d99e865d9b86c38a1b56e4a95d6e +Patch30: pam-1.5.1-pam-lastlog-file-locking.patch %global _pamlibdir %{_libdir} %global _moduledir %{_libdir}/security @@ -195,6 +200,8 @@ cp %{SOURCE18} . %patch26 -p1 -b .pam-inline-pam-asprintf %patch27 -p1 -b .pam-namespace-rebase %patch28 -p1 -b .pam-faillock-skip +%patch29 -p1 -b .pam-access-uid-gid-access-con +%patch30 -p1 -b .pam-lastlog-file-locking autoreconf -i @@ -450,6 +457,11 @@ done %doc doc/sag/*.txt doc/sag/html %changelog +* Thu Apr 9 2026 Iker Pedrosa - 1.5.1-29 +- pam_access: support UID and GID in access.conf + Resolves: RHEL-119868 +- pam_lastlog: fix file locking. Resolves: RHEL-118522 + * Wed Dec 10 2025 Iker Pedrosa - 1.5.1-28 - pam_faillock: skip clearing user's failed attempt. Resolves: RHEL-130875