File tcp_wrappers_7.6-ipv6-host-match.patch of Package tcpd

---
 hosts_access.c |  271 ++++++++++++++++++++++++++++++++++-----------------------
 misc.c         |   18 +++
 tcpd.h         |    2 
 3 files changed, 183 insertions(+), 108 deletions(-)

Index: tcp_wrappers_7.6/hosts_access.c
===================================================================
--- tcp_wrappers_7.6.orig/hosts_access.c
+++ tcp_wrappers_7.6/hosts_access.c
@@ -88,6 +88,10 @@ static int server_match();
 static int client_match();
 static int host_match();
 static int string_match();
+static int masked_match();
+#ifdef INET6
+static void ipv6_mask();
+#endif
 
 /* Size of logical line buffer. */
 
@@ -293,6 +297,74 @@ struct hosts_info *host;
     return (match);
 }
 
+static inline int
+host_info_ipv6addr(const struct host_info *host, struct in6_addr *addrbuf)
+{
+  /*
+   * In some cases we don't get the sockaddr, only the addr.
+   * We use inet_pton to convert it to its binary representation
+   * and match against that.
+   */
+  if (host->sin == NULL) {
+    if (host->addr == NULL || inet_pton(AF_INET6, host->addr, addrbuf) != 1)
+      return 0;
+
+    return 1;
+  } else
+  if (host->sin->ss_family == AF_INET6) {
+    *addrbuf = ((struct sockaddr_in6 *) host->sin)->sin6_addr;
+    return 1;
+  }
+  return 0;
+}
+
+static inline int
+token_ipv6addr_and_mask(char *tok, struct in6_addr *addrbuf, unsigned int *maskbits)
+{
+  char *cbr;
+  char *slash;
+
+  if (*tok != '[')
+    return 0;
+
+  *maskbits = 128;
+
+  ++tok; /* now points to the beginning of the IPv6 addr string */
+  if ((cbr = strchr(tok, ']')) == NULL) {
+    tcpd_warn("bad IP6 address specification");
+    return 0;
+  }
+  *cbr++ = '\0';
+
+  /*
+   * A /nnn prefix specifies how many bits of the address we
+   * need to check. 
+   * Note, we support both [x::y/64] and [x::y]/64
+   */
+  if ((slash = strchr(tok, '/')) == NULL && *cbr == '/')
+    slash = cbr;
+
+  if (slash != NULL) {
+    int mask;
+
+    *slash++ = '\0';
+    mask = atoi(slash);
+    if (mask < 0 || mask > 128) {
+      tcpd_warn("bad IP6 prefix specification");
+      return 0;
+    }
+
+    *maskbits = mask;
+  }
+
+  if (inet_pton(AF_INET6, tok, addrbuf) != 1) {
+    tcpd_warn("bad IP6 address specification");
+    return 0;
+  }
+
+  return 1;
+}
+
 /* host_match - match host name and/or address against pattern */
 
 static int host_match(tok, host)
@@ -328,12 +400,68 @@ struct host_info *host;
     } else if (STR_EQ(tok, "LOCAL")) {		/* local: no dots in name */
 	char   *name = eval_hostname(host);
 	return (strchr(name, '.') == 0 && HOSTNAME_KNOWN(name));
+    } else if (tok[0] == '[') {			/* IPv6 address */
+#ifdef INET6
+      struct in6_addr match_addr, host_addr;
+      unsigned int mask = 128;
+
+      if (!host_info_ipv6addr(host, &host_addr))
+        return (NO);
+
+      if (!token_ipv6addr_and_mask(tok, &match_addr, &mask))
+        return (NO);
+
+      /*
+       * Zero the bits we're not interested in in both addresses
+       * then compare.  Note that we take a copy of the host info
+       * in that case.
+       */
+      if (mask != 128) {
+	ipv6_mask(&match_addr, mask);
+	ipv6_mask(&host_addr, mask);
+      }
+      if (memcmp(&match_addr, &host_addr, sizeof(struct in6_addr)) == 0)
+	return (YES);
+#endif
+      return (NO);
+    } else if ((mask = split_at(tok, '/')) != 0) {	/* net/mask */
+      return (masked_match(tok, mask, eval_hostaddr(host)));
     } else {					/* anything else */
-	return (string_match(tok, eval_hostaddr(host))
-	    || (NOT_INADDR(tok) && string_match(tok, eval_hostname(host))));
+      return (string_match(tok, eval_hostaddr(host))
+           || (NOT_INADDR(tok) && string_match(tok, eval_hostname(host))));
     }
 }
 
+static int masked_match(net_tok, mask_tok, string)
+char   *net_tok;
+char   *mask_tok;
+char   *string;
+{
+    unsigned long net;
+    unsigned long mask;
+    unsigned long addr;
+ 
+    /*
+     * Disallow forms other than dotted quad: the treatment that inet_addr()
+     * gives to forms with less than four components is inconsistent with the
+     * access control language. John P. Rouillard <rouilj@cs.umb.edu>.
+     */
+ 
+    if ((addr = dot_quad_addr(string)) == INADDR_NONE)
+	return (NO);
+    if ((net = dot_quad_addr(net_tok)) == INADDR_NONE
+	|| ((mask = dot_quad_addr(mask_tok)) == INADDR_NONE
+	    && strcmp(mask_tok, "255.255.255.255")
+	    && (mask = prefix_to_netmask(mask_tok)) == INADDR_NONE
+	    && strcmp(mask_tok, "32"))) {
+	/* 255.255.255.255 == INADDR_NONE, separate check needed. TJ. */
+	/* 32 == INADDR_NONE, separate check needed. philipp */
+	tcpd_warn("bad net/mask expression: %s/%s", net_tok, mask_tok);
+	return (NO);				/* not tcpd_jump() */
+     }
+    return ((addr & mask) == net);
+}
+ 
 /* string_match - match string against pattern 
  * 
  * tok = data read from /etc/hosts.*
@@ -359,113 +487,14 @@ char   *string;
 	return (YES);
     } else if (STR_EQ(tok, "KNOWN")) {		/* not unknown */
 	return (STR_NE(string, unknown));
-    } else if (STR_EQ(tok, string))		/* exact match */
-	return (YES);
-#ifdef INET6
-    else	/* IP addresses match - not needed for IPv4 */
-    {
-	/* For simplicity we convert everything to IPv6 (or v4 mapped) */
-	struct in6_addr pat, addr;
-	int len, ret, prefixlen=128, nof_periods = 0;
-	char ch, token[INET6_ADDRSTRLEN+1], *mask, *addition;
-
-	/* If prefix was given, handle it */
-	if ((mask = split_at(tok, '/')) != 0)
-	{
-		if (strchr(mask, '.') != NULL) /* We have something
-                                                  like 255.255.0.0  */
-                {
-		   int b1, b2, b3, b4;
-		   uint32_t netmask;
-
-		   if (sscanf(mask, "%d.%d.%d.%d", &b1, &b2, &b3, &b4) != 4)
-		   {
-			tcpd_warn ("Wrong netmask in %s", tok);
-			return (NO);
-		   }
-		   netmask = (((((b1 * 256) + b2) * 256) + b3) * 256) + b4;
-		   prefixlen = 0;
-		   while (netmask > 0)
-		   {
-			++prefixlen;
-			netmask  <<= 1;
-                   }
-                }
-		else if (sscanf(mask, "%d", &prefixlen) != 1 || prefixlen < 0)
-		{
-			tcpd_warn ("Wrong prefix length in %s", tok);
-			return (NO);
-		}
-		
-		if (is_v4_string (tok))
-			prefixlen += 96;	/* extend to v4mapped */
-
-		if (prefixlen > 128)
-		{
-			tcpd_warn ("Prefix too long in %s", tok);
-			return (NO);
-		}
-	}
-
-	len = strlen(tok);
-	if (tok[len - 1] == '.') {	/* prefix */
-	  char *ptok = tok;
-
-	  while ((ptok = strchr(ptok, '.')) != NULL){
-	    nof_periods++;
-	    ptok++;
-	  }
-	  switch(nof_periods){
-	  case 1:
-	    addition = "0.0.0";
-	    prefixlen = 8;
-	    break;
-	  case 2:
-	    addition = "0.0";
-	    prefixlen = 16;
-	    break;
-	  case 3:
-	    addition = "0";
-	    prefixlen = 24;
-	    break;
-	  default: 
-	    tcpd_warn ("Wrong prefix %s", tok);
-	    return (NO);
-	  }
-	  snprintf(token, sizeof(token), "%s%s", tok, addition);
-	  prefixlen += 96;	/* extend to v4mapped */
-	}	
-	else if (*tok == '[' && tok[len - 1] == ']') 
-	{
-		ch = tok[len - 1];
-			tok[len - 1] = '\0';
-			snprintf(token, sizeof(token), "%s", tok+1);
-			tok[len - 1] = ch;
-	}
-	else
-		snprintf(token, sizeof(token), "%s", tok);
-	
-	memset (&pat, 0, sizeof(pat));
-	memset (&addr, 0, sizeof(addr));
-
-	if (inet_pton_mapped(AF_INET6, token, &pat) != 1)
-		return (NO);
-
-	if (inet_pton_mapped(AF_INET6, string, &addr) != 1)
-	{
-		tcpd_warn("Unable to handle client address: %s", string);
-		return (NO);
-	}
-
-	if (prefixlen < 128)
-	{
-		apply_v6_prefix (&pat, prefixlen);
-		apply_v6_prefix (&addr, prefixlen);
-	}
-
-	return (!memcmp(&pat, &addr, sizeof(struct in6_addr)));
+    } else if (tok[(n = strlen(tok)) - 1] == '.') {	/* prefix */
+	return (STRN_EQ(tok, string, n));
+    } else if ((STR_EQ(tok, "localhost") || STR_EQ(tok, "localhost.localdomain"))
+	    && (STR_EQ(string, "localhost") || STR_EQ(string, "localhost.localdomain"))) {
+	return (YES); /* these localhosts are equivalent */
+    } else {					/* exact match */
+	return (STR_EQ(tok, string));
     }
-#endif
 }
 
 #ifndef DISABLE_WILDCARD_MATCHING
@@ -535,3 +564,29 @@ int match_pattern_ylo(const char *s, con
   /*NOTREACHED*/
 }
 #endif /* DISABLE_WILDCARD_MATCHING */
+
+#ifdef INET6
+/*
+ * Function that zeros all but the first "maskbits" bits of the IPV6 address
+ * This function can be made generic by specifying an address length as
+ * extra parameter. (So Wietse can implement 1.2.3.4/16)
+ */
+static void ipv6_mask(in6p, maskbits)
+struct in6_addr *in6p;
+int maskbits;
+{
+    unsigned char *p = (unsigned char*) in6p;
+
+    if (maskbits < 0 || maskbits >= 128)
+	return;
+
+    p += maskbits / 8;
+    maskbits %= 8;
+
+    if (maskbits != 0)
+	*p++ &= 0xff << (8 - maskbits);
+
+    while (p < (((unsigned char*) in6p)) + sizeof(*in6p))
+	*p++ = 0;
+}
+#endif
Index: tcp_wrappers_7.6/misc.c
===================================================================
--- tcp_wrappers_7.6.orig/misc.c
+++ tcp_wrappers_7.6/misc.c
@@ -107,3 +107,21 @@ char   *str;
     }
     return (runs == 4 ? inet_addr(str) : INADDR_NONE);
 }
+
+/* prefix_to_netmask - convert prefix (0-32) to netmask */
+
+unsigned long prefix_to_netmask(str)
+char   *str;
+{
+    unsigned long prefix;
+    char *endptr;
+
+    if (!isdigit(str[0]))
+	return INADDR_NONE;
+
+    prefix = strtoul(str, &endptr, 10);
+    if ((endptr == str) || (*endptr != '\0') || (prefix > 32))
+	return INADDR_NONE;
+
+    return htonl(~0UL << (32 - prefix));
+}
Index: tcp_wrappers_7.6/tcpd.h
===================================================================
--- tcp_wrappers_7.6.orig/tcpd.h
+++ tcp_wrappers_7.6/tcpd.h
@@ -88,6 +88,7 @@ extern void refuse(struct request_info *
 extern char *xgets(char *, int, FILE *);
 extern char *split_at(char *, int);
 extern unsigned long dot_quad_addr(char *);
+extern char *skip_ipv6_addrs(char *);
 #else
 extern int hosts_access();		/* access control */
 extern void shell_cmd();		/* execute shell command */
@@ -98,6 +99,7 @@ extern void refuse();			/* clean up and
 extern char *xgets();			/* fgets() on steroids */
 extern char *split_at();		/* strchr() and split */
 extern unsigned long dot_quad_addr();	/* restricted inet_addr() */
+extern char *skip_ipv6_addrs();
 #endif
 
 /* Global variables. */
openSUSE Build Service is sponsored by