diff --git a/SOURCES/pam-1.5.1-pam-access-local.patch b/SOURCES/pam-1.5.1-pam-access-local.patch new file mode 100644 index 0000000..335b7f3 --- /dev/null +++ b/SOURCES/pam-1.5.1-pam-access-local.patch @@ -0,0 +1,215 @@ +From 08992030c56c940c0707ccbc442b1c325aa01e6d Mon Sep 17 00:00:00 2001 +From: Tomas Mraz +Date: Tue, 6 Apr 2021 12:27:38 +0200 +Subject: [PATCH] pam_access: clean up the remote host matching code + +* modules/pam_access/pam_access.c (from_match): Split out remote_match() + function and avoid calling it when matching against LOCAL keyword. + There is also no point in doing domain match against TTY or SERVICE. +--- + modules/pam_access/pam_access.c | 44 +++++++++++++++++++++------------ + 1 file changed, 28 insertions(+), 16 deletions(-) + +diff --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c +index 98848c54..277192b9 100644 +--- a/modules/pam_access/pam_access.c ++++ b/modules/pam_access/pam_access.c +@@ -160,6 +160,7 @@ static int list_match (pam_handle_t *, char *, char *, struct login_info *, + 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 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); + static int network_netmask_match (pam_handle_t *, const char *, const char *, struct login_info *); + +@@ -589,11 +590,9 @@ group_match (pam_handle_t *pamh, const char *tok, const char* usr, + /* from_match - match a host or tty against a list of tokens */ + + static int +-from_match (pam_handle_t *pamh UNUSED, char *tok, struct login_info *item) ++from_match (pam_handle_t *pamh, char *tok, struct login_info *item) + { + const char *string = item->from; +- int tok_len; +- int str_len; + int rv; + + if (item->debug) +@@ -616,14 +615,29 @@ from_match (pam_handle_t *pamh UNUSED, char *tok, struct login_info *item) + } else if ((rv = string_match(pamh, tok, string, item->debug)) != NO) { + /* ALL or exact match */ + return rv; +- } else if (tok[0] == '.') { /* domain: match last fields */ +- if ((str_len = strlen(string)) > (tok_len = strlen(tok)) +- && strcasecmp(tok, string + str_len - tok_len) == 0) +- return (YES); +- } else if (item->from_remote_host == 0) { /* local: no PAM_RHOSTS */ +- if (strcasecmp(tok, "LOCAL") == 0) +- return (YES); +- } else if (tok[(tok_len = strlen(tok)) - 1] == '.') { ++ } else if (strcasecmp(tok, "LOCAL") == 0) { ++ /* LOCAL matches only local accesses */ ++ if (!item->from_remote_host) ++ return YES; ++ return NO; ++ } else if (item->from_remote_host) { ++ return remote_match(pamh, tok, item); ++ } ++ return NO; ++} ++ ++static int ++remote_match (pam_handle_t *pamh, char *tok, struct login_info *item) ++{ ++ const char *string = item->from; ++ size_t tok_len = strlen(tok); ++ size_t str_len; ++ ++ if (tok[0] == '.') { /* domain: match last fields */ ++ if ((str_len = strlen(string)) > tok_len ++ && strcasecmp(tok, string + str_len - tok_len) == 0) ++ return YES; ++ } else if (tok[tok_len - 1] == '.') { + struct addrinfo hint; + + memset (&hint, '\0', sizeof (hint)); +@@ -661,13 +675,11 @@ from_match (pam_handle_t *pamh UNUSED, char *tok, struct login_info *item) + runp = runp->ai_next; + } + } +- } else { +- /* Assume network/netmask, IP address or hostname. */ +- if (network_netmask_match(pamh, tok, string, item)) +- return YES; ++ return NO; + } + +- return NO; ++ /* Assume network/netmask with an IP of a host. */ ++ return network_netmask_match(pamh, tok, string, item); + } + + /* string_match - match a string against one token */ +-- +2.47.0 + + +From ecaaf4456e5aeacae1acdb1775bb5aadd3b19e13 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +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 +--- + 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 +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 +--- + 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), ALL (which always matches) + or LOCAL. The LOCAL +- keyword matches if and only if +- pam_get_item3, +- when called with an item_type of +- PAM_RHOST, returns NULL or an +- empty string (and therefore the +- origins field is compared against the +- return value of +- pam_get_item3 +- called with an item_type of +- PAM_TTY or, absent that, +- PAM_SERVICE). ++ keyword matches when the user connects without a network ++ connection (e.g., su, ++ login). A connection through the loopback ++ device (e.g., ssh user@localhost) is ++ considered a network connection, and thus, the ++ LOCAL keyword does not match. + + + +-- +2.47.0 + diff --git a/SOURCES/pam-1.5.1-pam-access-resolve-ip.patch b/SOURCES/pam-1.5.1-pam-access-resolve-ip.patch new file mode 100644 index 0000000..f1442ba --- /dev/null +++ b/SOURCES/pam-1.5.1-pam-access-resolve-ip.patch @@ -0,0 +1,220 @@ +diff -up Linux-PAM-1.5.1/modules/pam_access/access.conf.5.xml.pam-access-resolve-ip 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-resolve-ip 2024-11-21 09:37:12.440595194 +0100 ++++ Linux-PAM-1.5.1/modules/pam_access/access.conf.5.xml 2024-11-21 09:39:18.568949578 +0100 +@@ -221,6 +221,14 @@ + item and the line will be most probably ignored. For this reason, it is not + recommended to put spaces around the ':' characters. + ++ ++ An IPv6 link local host address must contain the interface ++ identifier. IPv6 link local network/netmask is not supported. ++ ++ ++ Hostnames should be written as Fully-Qualified Host Name (FQHN) to avoid ++ confusion with device names or PAM service names. ++ + + + +diff -up Linux-PAM-1.5.1/modules/pam_access/pam_access.8.xml.pam-access-resolve-ip Linux-PAM-1.5.1/modules/pam_access/pam_access.8.xml +--- Linux-PAM-1.5.1/modules/pam_access/pam_access.8.xml.pam-access-resolve-ip 2024-11-21 09:37:12.440595194 +0100 ++++ Linux-PAM-1.5.1/modules/pam_access/pam_access.8.xml 2024-11-21 09:40:00.100066246 +0100 +@@ -25,11 +25,14 @@ + + debug + ++ ++ noaudit ++ + + nodefgroup + +- +- noaudit ++ ++ nodns + + + accessfile=file +@@ -114,7 +117,46 @@ + + + +- ++ nodefgroup ++ ++ ++ ++ User tokens which are not enclosed in parentheses will not be ++ matched against the group database. The backwards compatible default is ++ to try the group database match even for tokens not enclosed ++ in parentheses. ++ ++ ++ ++ ++ ++ ++ nodns ++ ++ ++ ++ Do not try to resolve tokens as hostnames, only IPv4 and IPv6 ++ addresses will be resolved. Which means to allow login from a ++ remote host, the IP addresses need to be specified in access.conf. ++ ++ ++ ++ ++ ++ ++ quiet_log ++ ++ ++ ++ Do not log denials with ++ syslog3. ++ ++ ++ ++ ++ ++ ++ fieldsep=separators + + + +@@ -152,20 +194,6 @@ + + + +- +- +- +- +- +- +- +- User tokens which are not enclosed in parentheses will not be +- matched against the group database. The backwards compatible default is +- to try the group database match even for tokens not enclosed +- in parentheses. +- +- +- + + + +diff -up Linux-PAM-1.5.1/modules/pam_access/pam_access.c.pam-access-resolve-ip Linux-PAM-1.5.1/modules/pam_access/pam_access.c +--- Linux-PAM-1.5.1/modules/pam_access/pam_access.c.pam-access-resolve-ip 2024-11-21 09:37:12.439595191 +0100 ++++ Linux-PAM-1.5.1/modules/pam_access/pam_access.c 2024-11-21 09:40:13.231103128 +0100 +@@ -92,6 +92,7 @@ struct login_info { + int debug; /* Print debugging messages. */ + int only_new_group_syntax; /* Only allow group entries of the form "(xyz)" */ + int noaudit; /* Do not audit denials */ ++ int nodns; /* Do not try to resolve tokens as hostnames */ + const char *fs; /* field separator */ + const char *sep; /* list-element separator */ + int from_remote_host; /* If PAM_RHOST was used for from */ +@@ -143,6 +144,8 @@ parse_args(pam_handle_t *pamh, struct lo + loginfo->only_new_group_syntax = YES; + } else if (strcmp (argv[i], "noaudit") == 0) { + loginfo->noaudit = YES; ++ } else if (strcmp (argv[i], "nodns") == 0) { ++ loginfo->nodns = YES; + } else { + pam_syslog(pamh, LOG_ERR, "unrecognized option [%s]", argv[i]); + } +@@ -663,7 +666,7 @@ remote_match (pam_handle_t *pamh, char * + if ((str_len = strlen(string)) > tok_len + && strcasecmp(tok, string + str_len - tok_len) == 0) + return YES; +- } else if (tok[tok_len - 1] == '.') { ++ } else if (tok[tok_len - 1] == '.') { /* internet network numbers/subnet (end with ".") */ + struct addrinfo hint; + + memset (&hint, '\0', sizeof (hint)); +@@ -738,6 +741,39 @@ string_match (pam_handle_t *pamh, const + } + + ++static int ++is_device (pam_handle_t *pamh, const char *tok) ++{ ++ struct stat st; ++ const char *dev = "/dev/"; ++ char *devname; ++ ++ devname = malloc (strlen(dev) + strlen (tok) + 1); ++ if (devname == NULL) { ++ pam_syslog(pamh, LOG_ERR, "Cannot allocate memory for device name: %m"); ++ /* ++ * We should return an error and abort, but pam_access has no good ++ * error handling. ++ */ ++ return NO; ++ } ++ ++ char *cp = stpcpy (devname, dev); ++ strcpy (cp, tok); ++ ++ if (lstat(devname, &st) != 0) ++ { ++ free (devname); ++ return NO; ++ } ++ free (devname); ++ ++ if (S_ISCHR(st.st_mode)) ++ return YES; ++ ++ return NO; ++} ++ + /* network_netmask_match - match a string against one token + * where string is a hostname or ip (v4,v6) address and tok + * represents either a hostname, a single ip (v4,v6) address +@@ -799,10 +835,42 @@ network_netmask_match (pam_handle_t *pam + return NO; + } + } ++ else if (isipaddr(tok, NULL, NULL) == YES) ++ { ++ if (getaddrinfo (tok, NULL, NULL, &ai) != 0) ++ { ++ if (item->debug) ++ pam_syslog(pamh, LOG_DEBUG, "cannot resolve IP address \"%s\"", tok); ++ ++ return NO; ++ } ++ netmask_ptr = NULL; ++ } ++ else if (item->nodns) ++ { ++ /* Only hostnames are left, which we would need to resolve via DNS */ ++ return NO; ++ } + else + { ++ /* Bail out on X11 Display entries and ttys. */ ++ if (tok[0] == ':') ++ { ++ if (item->debug) ++ pam_syslog (pamh, LOG_DEBUG, ++ "network_netmask_match: tok=%s is X11 display", tok); ++ return NO; ++ } ++ if (is_device (pamh, tok)) ++ { ++ if (item->debug) ++ pam_syslog (pamh, LOG_DEBUG, ++ "network_netmask_match: tok=%s is a TTY", tok); ++ return NO; ++ } ++ + /* +- * It is either an IP address or a hostname. ++ * It is most likely a hostname. + * Let getaddrinfo sort everything out + */ + if (getaddrinfo (tok, NULL, NULL, &ai) != 0) diff --git a/SPECS/pam.spec b/SPECS/pam.spec index 3a1eba4..c50a63c 100644 --- a/SPECS/pam.spec +++ b/SPECS/pam.spec @@ -3,7 +3,7 @@ Summary: An extensible library which provides authentication for applications Name: pam Version: 1.5.1 -Release: 21%{?dist} +Release: 23%{?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+. @@ -70,6 +70,11 @@ 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/08992030c56c940c0707ccbc442b1c325aa01e6d +# https://github.com/linux-pam/linux-pam/commit/641dfd1084508c63f3590e93a35b80ffc50774e5 +Patch23: pam-1.5.1-pam-access-local.patch +# https://github.com/linux-pam/linux-pam/commit/940747f88c16e029b69a74e80a2e94f65cb3e628 +Patch24: pam-1.5.1-pam-access-resolve-ip.patch %global _pamlibdir %{_libdir} %global _moduledir %{_libdir}/security @@ -174,6 +179,8 @@ cp %{SOURCE18} . %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 +%patch24 -p1 -b .pam-access-resolve-ip autoreconf -i @@ -429,9 +436,15 @@ done %doc doc/sag/*.txt doc/sag/html %changelog -* Mon Nov 4 2024 Iker Pedrosa - 1.5.1-21 +* Thu Nov 21 2024 Iker Pedrosa - 1.5.1-23 +- pam_access: rework resolving of tokens as hostname. + Resolves: CVE-2024-10963 and RHEL-66244 + +* Mon Nov 4 2024 Iker Pedrosa - 1.5.1-22 - 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 - 1.5.1-20 - libpam: support long lines in service files. Resolves: RHEL-40705