Re: pam_access.so not recognizing host name in access.conf

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Fedora Users]     [Kernel]     [Red Hat Install]     [Linux for the blind]     [Gimp]

  Powered by Linux