On 03.02.2017 14:52, Tomas Mraz wrote: > On Fri, 2017-01-27 at 14:09 +0100, Josef Moellers wrote: >> On 26.01.2017 16:40, Josef Moellers wrote: >>> Hi, >>> The following specification in access.conf does not work as >>> expected: >>> -:username:ALL EXCEPT localhost >>> The manual page access.conf.5 claims that the third field may >>> contain >>> host names, but the code only checks for numerical IP addresses by >>> calling inet_pton(). >>> Is this desired behavior or am I missing something. >>> I'm willing to write a patch. >> >> I suspect that "tok" and "string" need to be swapped in the second >> half >> of network_netmask_match(): > > No, this is not the case, the code is correct in what it is intended to > do. There is simply missing the matching for case where the application > calling the PAM module sets PAM_RHOST to an IP address (or an alias > name) and you want to use localhost in access.conf. > > I am not saying that support for this cannot be added but it is another > matching code to add, you cannot simply hijack network_netmask_match() > for that purpose. I have attached a patch which will build an addrinfo list from "tok" and match the given IP address ("string") against all the members of this list, returning YES on the first match. In the case of an IP address in access.conf, the list contains only a single member, the IP address itself, in case of a hostname, the list will contain all IP addresses the hostname resolves to. While going through the code, I also found one bug: char *endptr = NULL; netmask = strtol(netmask_ptr, &endptr, 0); - if ((endptr == NULL) || (*endptr != '\0')) + if ((endptr == netmask_ptr) || (*endptr != '\0')) { /* invalid netmask value */ return NO; >From the manual page of strtXXX: "If endptr is not NULL, strtol() stores the address of the first invalid character in *endptr. If there were no digits at all, strtol() stores the original value of nptr in *endptr (and returns 0)." Josef
Index: modules/pam_access/pam_access.c =================================================================== --- modules/pam_access/pam_access.c.orig +++ modules/pam_access/pam_access.c @@ -692,7 +692,6 @@ string_match (pam_handle_t *pamh, const 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 single ip (v4,v6) address or a network/netmask @@ -704,10 +703,15 @@ network_netmask_match (pam_handle_t *pam char *netmask_ptr; char netmask_string[MAXHOSTNAMELEN + 1]; int addr_type; + struct addrinfo *ai, tok_ai; + struct sockaddr sa; + struct sockaddr_storage tok_addr; + int do_free_ai = 0; 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) { @@ -717,7 +721,7 @@ network_netmask_match (pam_handle_t *pam *netmask_ptr = 0; netmask_ptr++; - if (isipaddr(tok, &addr_type, NULL) == NO) + if (isipaddr(tok, &addr_type, &tok_addr) == NO) { /* no netaddr */ return NO; } @@ -727,7 +731,7 @@ network_netmask_match (pam_handle_t *pam { /* netmask as integre value */ char *endptr = NULL; netmask = strtol(netmask_ptr, &endptr, 0); - if ((endptr == NULL) || (*endptr != '\0')) + if ((endptr == netmask_ptr) || (*endptr != '\0')) { /* invalid netmask value */ return NO; } @@ -739,13 +743,82 @@ network_netmask_match (pam_handle_t *pam netmask_ptr = number_to_netmask(netmask, addr_type, netmask_string, MAXHOSTNAMELEN); } - } + + memset(&tok_ai, 0, sizeof(struct addrinfo)); + tok_ai.ai_addr = &sa; + tok_ai.ai_family = addr_type; + if (addr_type == AF_INET) + { + struct sockaddr_in *sai = (struct sockaddr_in *)&sa; + + tok_ai.ai_addrlen = sizeof(sai->sin_addr); + memcpy(&sai->sin_addr, &tok_addr, sizeof(sai->sin_addr)); + } + else /* addr_type == AF_INET6 */ + { + struct sockaddr_in6 *sai = (struct sockaddr_in6 *)&sa; + + tok_ai.ai_addrlen = sizeof(sai->sin6_addr); + memcpy(&sai->sin6_addr, &tok_addr, sizeof(sai->sin6_addr)); + } + + ai = &tok_ai; + } + else if (isipaddr(tok, &addr_type, &tok_addr) == YES) + { + /* + * It is an IP address + * Hand-craft a single struct addrinfo + * which contains the IP address and + * which identifies the address as AF_INET or AF_INET6 + */ + memset(&tok_ai, 0, sizeof(struct addrinfo)); + tok_ai.ai_addr = (struct sockaddr *) &tok_addr; + tok_ai.ai_family = addr_type; + + if (addr_type == AF_INET) + { + struct sockaddr_in * sai = (struct sockaddr_in *)&sa; + + tok_ai.ai_addrlen = sizeof(sai->sin_addr); + memcpy(&sai->sin_addr, &tok_addr, sizeof(sai->sin_addr)); + } + else /* addr_type == AF_INET6 */ + { + struct sockaddr_in6 * sai = (struct sockaddr_in6 *)&sa; + + tok_ai.ai_addrlen = sizeof(sai->sin6_addr); + memcpy(&sai->sin6_addr, &tok_addr, sizeof(sai->sin6_addr)); + } + + ai = &tok_ai; + + netmask_ptr = NULL; + } else - /* NO, then check if it is only an addr */ - if (isipaddr(tok, NULL, NULL) != YES) + { + /* + * It is not an IP address, + * check if it is a host name + */ + struct addrinfo hint; + + memset (&hint, '\0', sizeof (hint)); + hint.ai_flags = AI_CANONNAME; + hint.ai_family = AF_UNSPEC; + + ai = NULL; /* just to be on the safe side */ + + if (getaddrinfo (string, NULL, &hint, &ai) != 0) { + pam_syslog(pamh, LOG_ERR, "cannot resolve hostname \"%s\"", string); + return NO; } + netmask_ptr = NULL; + + do_free_ai = 1; /* call freeaddrinfo() at the end */ + } if (isipaddr(string, NULL, NULL) != YES) { @@ -764,6 +837,7 @@ network_netmask_match (pam_handle_t *pam else { struct addrinfo *runp = item->res; + struct addrinfo *runp1; while (runp != NULL) { @@ -775,16 +849,53 @@ network_netmask_match (pam_handle_t *pam : (void *) &((struct sockaddr_in6 *) runp->ai_addr)->sin6_addr, buf, sizeof (buf)); - 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; + + inet_ntop (runp1->ai_family, + runp1->ai_family == AF_INET + ? (void *) &((struct sockaddr_in *) runp1->ai_addr)->sin_addr + : (void *) &((struct sockaddr_in6 *) runp1->ai_addr)->sin6_addr, + buf1, sizeof (buf1)); + if (are_addresses_equal(buf, buf1, netmask_ptr)) + { + if (do_free_ai) + 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]; + + inet_ntop (runp1->ai_family, + runp1->ai_family == AF_INET + ? (void *) &((struct sockaddr_in *) runp1->ai_addr)->sin_addr + : (void *) &((struct sockaddr_in6 *) runp1->ai_addr)->sin6_addr, + buf1, sizeof (buf1)); + if (are_addresses_equal(string, buf1, netmask_ptr)) + { + if (do_free_ai) + freeaddrinfo(ai); + return YES; + } + } + } + + if (ai != NULL && do_free_ai) + freeaddrinfo(ai); return NO; }
_______________________________________________ Pam-list mailing list Pam-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/pam-list