1From 23393bef92c1e768eda329813d7af55481c6ca9f Mon Sep 17 00:00:00 2001 2From: Thorsten Kukuk <kukuk@suse.com> 3Date: Thu, 24 Feb 2022 10:37:32 +0100 4Subject: [PATCH 2/2] pam_access: handle hostnames in access.conf 5 6According to the manual page, the following entry is valid but does not 7work: 8-:root:ALL EXCEPT localhost 9 10See https://bugzilla.suse.com/show_bug.cgi?id=1019866 11 12Patched is based on PR#226 from Josef Moellers 13 14Upstream-Status: Backport 15CVE: CVE-2022-28321 16 17Reference to upstream patch: 18[https://github.com/linux-pam/linux-pam/commit/23393bef92c1e768eda329813d7af55481c6ca9f] 19 20Signed-off-by: Stefan Ghinea <stefan.ghinea@windriver.com> 21--- 22 modules/pam_access/pam_access.c | 95 ++++++++++++++++++++++++++------- 23 1 file changed, 76 insertions(+), 19 deletions(-) 24 25diff --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c 26index 277192b..bca424f 100644 27--- a/modules/pam_access/pam_access.c 28+++ b/modules/pam_access/pam_access.c 29@@ -637,7 +637,7 @@ remote_match (pam_handle_t *pamh, char *tok, struct login_info *item) 30 if ((str_len = strlen(string)) > tok_len 31 && strcasecmp(tok, string + str_len - tok_len) == 0) 32 return YES; 33- } else if (tok[tok_len - 1] == '.') { 34+ } else if (tok[tok_len - 1] == '.') { /* internet network numbers (end with ".") */ 35 struct addrinfo hint; 36 37 memset (&hint, '\0', sizeof (hint)); 38@@ -678,7 +678,7 @@ remote_match (pam_handle_t *pamh, char *tok, struct login_info *item) 39 return NO; 40 } 41 42- /* Assume network/netmask with an IP of a host. */ 43+ /* Assume network/netmask, IP address or hostname. */ 44 return network_netmask_match(pamh, tok, string, item); 45 } 46 47@@ -696,7 +696,7 @@ string_match (pam_handle_t *pamh, const char *tok, const char *string, 48 /* 49 * If the token has the magic value "ALL" the match always succeeds. 50 * Otherwise, return YES if the token fully matches the string. 51- * "NONE" token matches NULL string. 52+ * "NONE" token matches NULL string. 53 */ 54 55 if (strcasecmp(tok, "ALL") == 0) { /* all: always matches */ 56@@ -714,7 +714,8 @@ string_match (pam_handle_t *pamh, const char *tok, const char *string, 57 58 /* network_netmask_match - match a string against one token 59 * where string is a hostname or ip (v4,v6) address and tok 60- * represents either a single ip (v4,v6) address or a network/netmask 61+ * represents either a hostname, a single ip (v4,v6) address 62+ * or a network/netmask 63 */ 64 static int 65 network_netmask_match (pam_handle_t *pamh, 66@@ -723,10 +724,12 @@ network_netmask_match (pam_handle_t *pamh, 67 char *netmask_ptr; 68 char netmask_string[MAXHOSTNAMELEN + 1]; 69 int addr_type; 70+ struct addrinfo *ai = NULL; 71 72 if (item->debug) 73- pam_syslog (pamh, LOG_DEBUG, 74+ pam_syslog (pamh, LOG_DEBUG, 75 "network_netmask_match: tok=%s, item=%s", tok, string); 76+ 77 /* OK, check if tok is of type addr/mask */ 78 if ((netmask_ptr = strchr(tok, '/')) != NULL) 79 { 80@@ -760,54 +763,108 @@ network_netmask_match (pam_handle_t *pamh, 81 netmask_ptr = number_to_netmask(netmask, addr_type, 82 netmask_string, MAXHOSTNAMELEN); 83 } 84- } 85+ 86+ /* 87+ * Construct an addrinfo list from the IP address. 88+ * This should not fail as the input is a correct IP address... 89+ */ 90+ if (getaddrinfo (tok, NULL, NULL, &ai) != 0) 91+ { 92+ return NO; 93+ } 94+ } 95 else 96- /* NO, then check if it is only an addr */ 97- if (isipaddr(tok, NULL, NULL) != YES) 98+ { 99+ /* 100+ * It is either an IP address or a hostname. 101+ * Let getaddrinfo sort everything out 102+ */ 103+ if (getaddrinfo (tok, NULL, NULL, &ai) != 0) 104 { 105+ pam_syslog(pamh, LOG_ERR, "cannot resolve hostname \"%s\"", tok); 106+ 107 return NO; 108 } 109+ netmask_ptr = NULL; 110+ } 111 112 if (isipaddr(string, NULL, NULL) != YES) 113 { 114- /* Assume network/netmask with a name of a host. */ 115 struct addrinfo hint; 116 117+ /* Assume network/netmask with a name of a host. */ 118 memset (&hint, '\0', sizeof (hint)); 119 hint.ai_flags = AI_CANONNAME; 120 hint.ai_family = AF_UNSPEC; 121 122 if (item->gai_rv != 0) 123+ { 124+ freeaddrinfo(ai); 125 return NO; 126+ } 127 else if (!item->res && 128 (item->gai_rv = getaddrinfo (string, NULL, &hint, &item->res)) != 0) 129+ { 130+ freeaddrinfo(ai); 131 return NO; 132+ } 133 else 134 { 135 struct addrinfo *runp = item->res; 136+ struct addrinfo *runp1; 137 138 while (runp != NULL) 139 { 140 char buf[INET6_ADDRSTRLEN]; 141 142- DIAG_PUSH_IGNORE_CAST_ALIGN; 143- inet_ntop (runp->ai_family, 144- runp->ai_family == AF_INET 145- ? (void *) &((struct sockaddr_in *) runp->ai_addr)->sin_addr 146- : (void *) &((struct sockaddr_in6 *) runp->ai_addr)->sin6_addr, 147- buf, sizeof (buf)); 148- DIAG_POP_IGNORE_CAST_ALIGN; 149+ if (getnameinfo (runp->ai_addr, runp->ai_addrlen, buf, sizeof (buf), NULL, 0, NI_NUMERICHOST) != 0) 150+ { 151+ freeaddrinfo(ai); 152+ return NO; 153+ } 154 155- if (are_addresses_equal(buf, tok, netmask_ptr)) 156+ for (runp1 = ai; runp1 != NULL; runp1 = runp1->ai_next) 157 { 158- return YES; 159+ char buf1[INET6_ADDRSTRLEN]; 160+ 161+ if (runp->ai_family != runp1->ai_family) 162+ continue; 163+ 164+ if (getnameinfo (runp1->ai_addr, runp1->ai_addrlen, buf1, sizeof (buf1), NULL, 0, NI_NUMERICHOST) != 0) 165+ { 166+ freeaddrinfo(ai); 167+ return NO; 168+ } 169+ 170+ if (are_addresses_equal (buf, buf1, netmask_ptr)) 171+ { 172+ freeaddrinfo(ai); 173+ return YES; 174+ } 175 } 176 runp = runp->ai_next; 177 } 178 } 179 } 180 else 181- return (are_addresses_equal(string, tok, netmask_ptr)); 182+ { 183+ struct addrinfo *runp1; 184+ 185+ for (runp1 = ai; runp1 != NULL; runp1 = runp1->ai_next) 186+ { 187+ char buf1[INET6_ADDRSTRLEN]; 188+ 189+ (void) getnameinfo (runp1->ai_addr, runp1->ai_addrlen, buf1, sizeof (buf1), NULL, 0, NI_NUMERICHOST); 190+ 191+ if (are_addresses_equal(string, buf1, netmask_ptr)) 192+ { 193+ freeaddrinfo(ai); 194+ return YES; 195+ } 196+ } 197+ } 198+ 199+ freeaddrinfo(ai); 200 201 return NO; 202 } 203-- 2042.37.3 205 206