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