diff --git a/backport-CVE-2024-10963.patch b/backport-CVE-2024-10963.patch new file mode 100644 index 0000000000000000000000000000000000000000..5bc3c33391faec40cd75bb12e14b00dd54fc6796 --- /dev/null +++ b/backport-CVE-2024-10963.patch @@ -0,0 +1,228 @@ +From 940747f88c16e029b69a74e80a2e94f65cb3e628 Mon Sep 17 00:00:00 2001 +From: Thorsten Kukuk +Date: Thu, 14 Nov 2024 10:27:28 +0100 +Subject: [PATCH] pam_access: rework resolving of tokens as hostname + +Conflict:Context adaptation +Reference:https://github.com/linux-pam/linux-pam/commit/940747f88c16e029b69a74e80a2e94f65cb3e628 + +* modules/pam_access/pam_access.c: separate resolving of IP addresses + from hostnames. Don't resolve TTYs or display variables as hostname + (#834). + Add "nodns" option to disallow resolving of tokens as hostname. +* modules/pam_access/pam_access.8.xml: document nodns option +* modules/pam_access/access.conf.5.xml: document that hostnames should + be written as FQHN. +--- + modules/pam_access/access.conf.5.xml | 4 ++ + modules/pam_access/pam_access.8.xml | 46 ++++++++++++------ + modules/pam_access/pam_access.c | 72 +++++++++++++++++++++++++++- + 3 files changed, 105 insertions(+), 17 deletions(-) + +diff --git a/modules/pam_access/access.conf.5.xml b/modules/pam_access/access.conf.5.xml +index 0b93db00..10b8ba92 100644 +--- a/modules/pam_access/access.conf.5.xml ++++ b/modules/pam_access/access.conf.5.xml +@@ -226,6 +226,10 @@ + item and the line will be most probably ignored. For this reason, it is not + recommended to put spaces around the ':' characters. + ++ ++ Hostnames should be written as Fully-Qualified Host Name (FQHN) to avoid ++ confusion with device names or PAM service names. ++ + + + +diff --git a/modules/pam_access/pam_access.8.xml b/modules/pam_access/pam_access.8.xml +index c991d7a0..71a4f7ee 100644 +--- a/modules/pam_access/pam_access.8.xml ++++ b/modules/pam_access/pam_access.8.xml +@@ -25,11 +25,14 @@ + + debug + ++ ++ noaudit ++ + + nodefgroup + + +- noaudit ++ nodns + + + accessfile=file +@@ -112,6 +115,33 @@ + + + ++ ++ ++ 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. ++ ++ ++ ++ + + + +@@ -153,20 +183,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 --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c +index 48e7c7e9..109115e9 100644 +--- a/modules/pam_access/pam_access.c ++++ b/modules/pam_access/pam_access.c +@@ -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 login_info *loginfo, + 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]); + } +@@ -637,7 +640,7 @@ remote_match (pam_handle_t *pamh, char *tok, struct login_info *item) + if ((str_len = strlen(string)) > tok_len + && strcasecmp(tok, string + str_len - tok_len) == 0) + return YES; +- } else if (tok[tok_len - 1] == '.') { /* internet network numbers (end with ".") */ ++ } else if (tok[tok_len - 1] == '.') { /* internet network numbers/subnet (end with ".") */ + struct addrinfo hint; + + memset (&hint, '\0', sizeof (hint)); +@@ -712,6 +715,39 @@ string_match (pam_handle_t *pamh, const char *tok, const char *string, + } + + ++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 +@@ -773,10 +809,42 @@ network_netmask_match (pam_handle_t *pamh, + 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) +-- +2.33.0 + diff --git a/backport-pam_access-clean-up-the-remote-host-matching-code.patch b/backport-pam_access-clean-up-the-remote-host-matching-code.patch new file mode 100644 index 0000000000000000000000000000000000000000..954fe9d3c6d45a2a5a9d3228b8bc5ca8122fa24f --- /dev/null +++ b/backport-pam_access-clean-up-the-remote-host-matching-code.patch @@ -0,0 +1,98 @@ +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 + +Conflict:NA +Reference:https://github.com/linux-pam/linux-pam/commit/08992030c56c940c0707ccbc442b1c325aa01e6d + +* 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 with a IP of a host. */ +- 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.33.0 + diff --git a/backport-pam_access-handle-hostnames-in-access.conf.patch b/backport-pam_access-handle-hostnames-in-access.conf.patch new file mode 100644 index 0000000000000000000000000000000000000000..e3e28ea37735e7b80882599b119ca91c5551cf72 --- /dev/null +++ b/backport-pam_access-handle-hostnames-in-access.conf.patch @@ -0,0 +1,200 @@ +From 23393bef92c1e768eda329813d7af55481c6ca9f Mon Sep 17 00:00:00 2001 +From: Thorsten Kukuk +Date: Thu, 24 Feb 2022 10:37:32 +0100 +Subject: [PATCH] pam_access: handle hostnames in access.conf + +Conflict:NA +Reference:https://github.com/linux-pam/linux-pam/commit/23393bef92c1e768eda329813d7af55481c6ca9f + +According to the manual page, the following entry is valid but does not +work: +-:root:ALL EXCEPT localhost + +See https://bugzilla.suse.com/show_bug.cgi?id=1019866 + +Patched is based on PR#226 from Josef Moellers +--- + modules/pam_access/pam_access.c | 95 ++++++++++++++++++++++++++------- + 1 file changed, 76 insertions(+), 19 deletions(-) + +diff --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c +index 0d033aa2..3cec542b 100644 +--- a/modules/pam_access/pam_access.c ++++ b/modules/pam_access/pam_access.c +@@ -640,7 +640,7 @@ remote_match (pam_handle_t *pamh, char *tok, struct login_info *item) + 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 (end with ".") */ + struct addrinfo hint; + + memset (&hint, '\0', sizeof (hint)); +@@ -681,7 +681,7 @@ remote_match (pam_handle_t *pamh, char *tok, struct login_info *item) + return NO; + } + +- /* Assume network/netmask with an IP of a host. */ ++ /* Assume network/netmask, IP address or hostname. */ + return network_netmask_match(pamh, tok, string, item); + } + +@@ -699,7 +699,7 @@ string_match (pam_handle_t *pamh, const char *tok, const char *string, + /* + * If the token has the magic value "ALL" the match always succeeds. + * Otherwise, return YES if the token fully matches the string. +- * "NONE" token matches NULL string. ++ * "NONE" token matches NULL string. + */ + + if (strcasecmp(tok, "ALL") == 0) { /* all: always matches */ +@@ -717,7 +717,8 @@ string_match (pam_handle_t *pamh, const char *tok, const char *string, + + /* network_netmask_match - match a string against one token + * where string is a hostname or ip (v4,v6) address and tok +- * represents either a single ip (v4,v6) address or a network/netmask ++ * represents either a hostname, a single ip (v4,v6) address ++ * or a network/netmask + */ + static int + network_netmask_match (pam_handle_t *pamh, +@@ -726,10 +727,12 @@ network_netmask_match (pam_handle_t *pamh, + char *netmask_ptr; + char netmask_string[MAXHOSTNAMELEN + 1]; + int addr_type; ++ struct addrinfo *ai = NULL; + + if (item->debug) +- pam_syslog (pamh, LOG_DEBUG, ++ pam_syslog (pamh, LOG_DEBUG, + "network_netmask_match: tok=%s, item=%s", tok, string); ++ + /* OK, check if tok is of type addr/mask */ + if ((netmask_ptr = strchr(tok, '/')) != NULL) + { +@@ -763,54 +766,108 @@ network_netmask_match (pam_handle_t *pamh, + netmask_ptr = number_to_netmask(netmask, addr_type, + netmask_string, MAXHOSTNAMELEN); + } +- } ++ ++ /* ++ * Construct an addrinfo list from the IP address. ++ * This should not fail as the input is a correct IP address... ++ */ ++ if (getaddrinfo (tok, NULL, NULL, &ai) != 0) ++ { ++ return NO; ++ } ++ } + else +- /* NO, then check if it is only an addr */ +- if (isipaddr(tok, NULL, NULL) != YES) ++ { ++ /* ++ * It is either an IP address or a hostname. ++ * Let getaddrinfo sort everything out ++ */ ++ if (getaddrinfo (tok, NULL, NULL, &ai) != 0) + { ++ pam_syslog(pamh, LOG_ERR, "cannot resolve hostname \"%s\"", tok); ++ + return NO; + } ++ netmask_ptr = NULL; ++ } + + if (isipaddr(string, NULL, NULL) != YES) + { +- /* Assume network/netmask with a name of a host. */ + struct addrinfo hint; + ++ /* Assume network/netmask with a name of a host. */ + memset (&hint, '\0', sizeof (hint)); + hint.ai_flags = AI_CANONNAME; + hint.ai_family = AF_UNSPEC; + + if (item->gai_rv != 0) ++ { ++ freeaddrinfo(ai); + return NO; ++ } + else if (!item->res && + (item->gai_rv = getaddrinfo (string, NULL, &hint, &item->res)) != 0) ++ { ++ freeaddrinfo(ai); + return NO; ++ } + else + { + struct addrinfo *runp = item->res; ++ struct addrinfo *runp1; + + while (runp != NULL) + { + char buf[INET6_ADDRSTRLEN]; + +- DIAG_PUSH_IGNORE_CAST_ALIGN; +- inet_ntop (runp->ai_family, +- runp->ai_family == AF_INET +- ? (void *) &((struct sockaddr_in *) runp->ai_addr)->sin_addr +- : (void *) &((struct sockaddr_in6 *) runp->ai_addr)->sin6_addr, +- buf, sizeof (buf)); +- DIAG_POP_IGNORE_CAST_ALIGN; ++ if (getnameinfo (runp->ai_addr, runp->ai_addrlen, buf, sizeof (buf), NULL, 0, NI_NUMERICHOST) != 0) ++ { ++ freeaddrinfo(ai); ++ return NO; ++ } + +- if (are_addresses_equal(buf, tok, netmask_ptr)) ++ for (runp1 = ai; runp1 != NULL; runp1 = runp1->ai_next) + { +- return YES; ++ char buf1[INET6_ADDRSTRLEN]; ++ ++ if (runp->ai_family != runp1->ai_family) ++ continue; ++ ++ if (getnameinfo (runp1->ai_addr, runp1->ai_addrlen, buf1, sizeof (buf1), NULL, 0, NI_NUMERICHOST) != 0) ++ { ++ freeaddrinfo(ai); ++ return NO; ++ } ++ ++ if (are_addresses_equal (buf, buf1, netmask_ptr)) ++ { ++ freeaddrinfo(ai); ++ return YES; ++ } + } + runp = runp->ai_next; + } + } + } + else +- return (are_addresses_equal(string, tok, netmask_ptr)); ++ { ++ struct addrinfo *runp1; ++ ++ for (runp1 = ai; runp1 != NULL; runp1 = runp1->ai_next) ++ { ++ char buf1[INET6_ADDRSTRLEN]; ++ ++ (void) getnameinfo (runp1->ai_addr, runp1->ai_addrlen, buf1, sizeof (buf1), NULL, 0, NI_NUMERICHOST); ++ ++ if (are_addresses_equal(string, buf1, netmask_ptr)) ++ { ++ freeaddrinfo(ai); ++ return YES; ++ } ++ } ++ } ++ ++ freeaddrinfo(ai); + + return NO; + } +-- +2.33.0 + diff --git a/backport-pam_access-make-non-resolveable-hostname-a-debug-out.patch b/backport-pam_access-make-non-resolveable-hostname-a-debug-out.patch new file mode 100644 index 0000000000000000000000000000000000000000..e3e2e8bcb99298c330d5aea2ca792d4aaab4b68d --- /dev/null +++ b/backport-pam_access-make-non-resolveable-hostname-a-debug-out.patch @@ -0,0 +1,33 @@ +From 741acf4ff707d53b94947736a01eeeda5e2c7e98 Mon Sep 17 00:00:00 2001 +From: Thorsten Kukuk +Date: Fri, 4 Aug 2023 15:46:16 +0200 +Subject: [PATCH] pam_access: make non-resolveable hostname a debug output + (#590) + +Conflict:NA +Reference:https://github.com/linux-pam/linux-pam/commit/741acf4ff707d53b94947736a01eeeda5e2c7e98 + +* modules/pam_access/pam_access.c (network_netmask_match): Don't print +an error if a string is not resolveable, only a debug message in debug +mode. We even don't know if that entry is for remote logins or not. +--- + modules/pam_access/pam_access.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c +index f70b7e49..985dc7de 100644 +--- a/modules/pam_access/pam_access.c ++++ b/modules/pam_access/pam_access.c +@@ -876,7 +876,8 @@ network_netmask_match (pam_handle_t *pamh, + */ + if (getaddrinfo (tok, NULL, NULL, &ai) != 0) + { +- pam_syslog(pamh, LOG_ERR, "cannot resolve hostname \"%s\"", tok); ++ if (item->debug) ++ pam_syslog(pamh, LOG_DEBUG, "cannot resolve hostname \"%s\"", tok); + + return NO; + } +-- +2.33.0 + diff --git a/pam.spec b/pam.spec index d40e0c12c4417994fcf61534378fa0378be61674..cc78f473b86a32493310baf599de71b7d4111561 100644 --- a/pam.spec +++ b/pam.spec @@ -4,7 +4,7 @@ %define _pamconfdir %{_sysconfdir}/pam.d Name: pam Version: 1.4.0 -Release: 11 +Release: 12 Summary: Pluggable Authentication Modules for Linux License: BSD and GPLv2+ URL: http://www.linux-pam.org/ @@ -33,6 +33,10 @@ Patch6000: backport-Move-read_passwords-function-from-pam_unix-to-pam_inline.h.p Patch6001: backport-add-helper-to-handle-SELinux.patch Patch6002: zh_CN_po_fix_str_meaning_error.patch Patch6003: backport-po-update-translations-using-Weblate-Chinese-Simplif.patch +Patch6004: backport-pam_access-clean-up-the-remote-host-matching-code.patch +Patch6005: backport-pam_access-handle-hostnames-in-access.conf.patch +Patch6006: backport-pam_access-make-non-resolveable-hostname-a-debug-out.patch +Patch6007: backport-CVE-2024-10963.patch Patch9000: add-sm3-crypt-support.patch @@ -187,6 +191,9 @@ fi %changelog +* Fri Nov 29 2024 hugel - 1.4.0-12 +- fix CVE-2024-10963 + * Wed Jan 24 2024 zhangruifang - 1.4.0-11 - fix CVE-2024-22365