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