]> granicus.if.org Git - postgresql/commitdiff
Allow IPv4-format entries in pg_hba.conf to match IPv6 connections
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 5 Sep 2003 20:31:36 +0000 (20:31 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 5 Sep 2003 20:31:36 +0000 (20:31 +0000)
that have IPv4-embedded-in-IPv6 addresses.  Per idea of Andreas Pflug.

doc/src/sgml/client-auth.sgml
src/backend/libpq/hba.c
src/backend/libpq/ip.c
src/backend/libpq/pg_hba.conf.sample
src/include/libpq/ip.h

index 2ad7a6a29fc3fd17d328858268d58987096691bd..481957e4ee17b09fb06c329ff027b883cc62e3af 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.56 2003/08/31 17:32:18 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.57 2003/09/05 20:31:35 tgl Exp $
 -->
 
 <chapter id="client-authentication">
@@ -199,13 +199,17 @@ hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
 <programlisting>
 (<replaceable>actual-IP-address</replaceable> xor <replaceable>IP-address-field</replaceable>) and <replaceable>IP-mask-field</replaceable>
 </programlisting>
-       must be zero for the record to match.  (Of course IP addresses
-       can be spoofed but this consideration is beyond the scope of
-       <productname>PostgreSQL</productname>.) If you machine supports
-       IPv6, the default <filename>pg_hba.conf</> file will have an
-       IPv6 entry for <literal>localhost</>. You can add your own IPv6
-       entries to the file. IPv6 entries are used only for IPv6
-       connections.
+       must be zero for the record to match.
+      </para>
+
+      <para>
+       An IP address given in IPv4 format will match IPv6 connections that
+       have the corresponding address, for example <literal>127.0.0.1</>
+       will match the IPv6 address <literal>::ffff:127.0.0.1</>.  An entry
+       given in IPv6 format will match only IPv6 connections, even if the
+       represented address is in the IPv4-in-IPv6 range.  Note that entries
+       in IPv6 format will be rejected if the system's C library does not have
+       support for IPv6 addresses.
       </para>
 
       <para>
@@ -219,9 +223,10 @@ hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
      <term><replaceable>CIDR-mask</replaceable></term>
      <listitem>
       <para>
-        This is an integer specifying the number of significant bits 
-        to set in the mask, and is an alternative to using the 
-        <replaceable>IP-mask</replaceable> notation. The number must
+        This field may be used as an alternative to the 
+        <replaceable>IP-mask</replaceable> notation.  It is an
+       integer specifying the number of high-order bits 
+        to set in the mask.  The number must
         be between 0 and 32 (in the case of an IPv4 address) or 128
         (in the case of an IPv6 address) inclusive. 0 will match any
         address, while 32/128 will match only the exact host specified.
index 4a677585625cf2bdf3b4e748129cc9063745c508..2e2126362118176b724cf72b1c58748f056bcab7 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.112 2003/09/05 03:57:13 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.113 2003/09/05 20:31:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -673,13 +673,6 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
                if (cidr_slash)
                        *cidr_slash = '/';
 
-               if (file_ip_addr->ai_family != port->raddr.addr.ss_family)
-               {
-                       /* Wrong address family. */
-                       freeaddrinfo_all(hints.ai_family, file_ip_addr);
-                       return;
-               }
-
                /* Get the netmask */
                if (cidr_slash)
                {
@@ -705,6 +698,28 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
                                goto hba_syntax;
                }
 
+               if (file_ip_addr->ai_family != port->raddr.addr.ss_family)
+               {
+                       /*
+                        * Wrong address family.  We allow only one case: if the
+                        * file has IPv4 and the port is IPv6, promote the file
+                        * address to IPv6 and try to match that way.
+                        */
+#ifdef HAVE_IPV6
+                       if (file_ip_addr->ai_family == AF_INET &&
+                               port->raddr.addr.ss_family == AF_INET6)
+                       {
+                               promote_v4_to_v6_addr((struct sockaddr_storage *) file_ip_addr->ai_addr);
+                               promote_v4_to_v6_mask(mask);
+                       }
+                       else
+#endif /* HAVE_IPV6 */
+                       {
+                               freeaddrinfo_all(hints.ai_family, file_ip_addr);
+                               return;
+                       }
+               }
+
                /* Read the rest of the line. */
                line = lnext(line);
                if (!line)
index 8ae6c8f7848bc5a07c17095d3af9eb4e3567c793..6ab5f57fd785494555e68ef24116692a52796905 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/libpq/ip.c,v 1.19 2003/08/04 02:39:59 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/libpq/ip.c,v 1.20 2003/09/05 20:31:36 tgl Exp $
  *
  * This file and the IPV6 implementation were initially provided by
  * Nigel Kukard <nkukard@lbsd.net>, Linux Based Systems Design
@@ -34,7 +34,8 @@
 #endif
 #include <arpa/inet.h>
 #include <sys/file.h>
-#endif
+
+#endif /* !defined(_MSC_VER) && !defined(__BORLANDC__) */
 
 #include "libpq/ip.h"
 
@@ -265,9 +266,16 @@ getnameinfo_unix(const struct sockaddr_un * sa, int salen,
 
        return 0;
 }
+
 #endif   /* HAVE_UNIX_SOCKETS */
 
 
+/*
+ * rangeSockAddr - is addr within the subnet specified by netaddr/netmask ?
+ *
+ * Note: caller must already have verified that all three addresses are
+ * in the same address family; and AF_UNIX addresses are not supported.
+ */
 int
 rangeSockAddr(const struct sockaddr_storage * addr,
                          const struct sockaddr_storage * netaddr,
@@ -287,6 +295,39 @@ rangeSockAddr(const struct sockaddr_storage * addr,
                return 0;
 }
 
+static int
+rangeSockAddrAF_INET(const struct sockaddr_in * addr,
+                                        const struct sockaddr_in * netaddr,
+                                        const struct sockaddr_in * netmask)
+{
+       if (((addr->sin_addr.s_addr ^ netaddr->sin_addr.s_addr) &
+                netmask->sin_addr.s_addr) == 0)
+               return 1;
+       else
+               return 0;
+}
+
+
+#ifdef HAVE_IPV6
+static int
+rangeSockAddrAF_INET6(const struct sockaddr_in6 * addr,
+                                         const struct sockaddr_in6 * netaddr,
+                                         const struct sockaddr_in6 * netmask)
+{
+       int                     i;
+
+       for (i = 0; i < 16; i++)
+       {
+               if (((addr->sin6_addr.s6_addr[i] ^ netaddr->sin6_addr.s6_addr[i]) &
+                        netmask->sin6_addr.s6_addr[i]) != 0)
+                       return 0;
+       }
+
+       return 1;
+}
+
+#endif
+
 /*
  *     SockAddr_cidr_mask - make a network mask of the appropriate family
  *       and required number of significant bits
@@ -358,34 +399,74 @@ SockAddr_cidr_mask(struct sockaddr_storage ** mask, char *numbits, int family)
        return 0;
 }
 
-static int
-rangeSockAddrAF_INET(const struct sockaddr_in * addr, const struct sockaddr_in * netaddr,
-                                        const struct sockaddr_in * netmask)
+
+#ifdef HAVE_IPV6
+
+/*
+ * promote_v4_to_v6_addr --- convert an AF_INET addr to AF_INET6, using
+ *             the standard convention for IPv4 addresses mapped into IPv6 world
+ *
+ * The passed addr is modified in place.  Note that we only worry about
+ * setting the fields that rangeSockAddr will look at.
+ */
+void
+promote_v4_to_v6_addr(struct sockaddr_storage * addr)
 {
-       if (((addr->sin_addr.s_addr ^ netaddr->sin_addr.s_addr) &
-                netmask->sin_addr.s_addr) == 0)
-               return 1;
-       else
-               return 0;
-}
+       struct sockaddr_in addr4;
+       struct sockaddr_in6 addr6;
+       uint32          s_addr;
 
+       memcpy(&addr4, addr, sizeof(addr4));
+       s_addr = ntohl(addr4.sin_addr.s_addr);
 
-#ifdef HAVE_IPV6
-static int
-rangeSockAddrAF_INET6(const struct sockaddr_in6 * addr,
-                                         const struct sockaddr_in6 * netaddr,
-                                         const struct sockaddr_in6 * netmask)
+       memset(&addr6, 0, sizeof(addr6));
+
+       addr6.sin6_family = AF_INET6;
+
+       addr6.sin6_addr.s6_addr[10] = 0xff;
+       addr6.sin6_addr.s6_addr[11] = 0xff;
+       addr6.sin6_addr.s6_addr[12] = (s_addr >> 24) & 0xFF;
+       addr6.sin6_addr.s6_addr[13] = (s_addr >> 16) & 0xFF;
+       addr6.sin6_addr.s6_addr[14] = (s_addr >> 8) & 0xFF;
+       addr6.sin6_addr.s6_addr[15] = (s_addr) & 0xFF;
+
+       memcpy(addr, &addr6, sizeof(addr6));
+}
+
+/*
+ * promote_v4_to_v6_mask --- convert an AF_INET netmask to AF_INET6, using
+ *             the standard convention for IPv4 addresses mapped into IPv6 world
+ *
+ * This must be different from promote_v4_to_v6_addr because we want to
+ * set the high-order bits to 1's not 0's.
+ *
+ * The passed addr is modified in place.  Note that we only worry about
+ * setting the fields that rangeSockAddr will look at.
+ */
+void
+promote_v4_to_v6_mask(struct sockaddr_storage * addr)
 {
+       struct sockaddr_in addr4;
+       struct sockaddr_in6 addr6;
+       uint32          s_addr;
        int                     i;
 
-       for (i = 0; i < 16; i++)
-       {
-               if (((addr->sin6_addr.s6_addr[i] ^ netaddr->sin6_addr.s6_addr[i]) &
-                        netmask->sin6_addr.s6_addr[i]) != 0)
-                       return 0;
-       }
+       memcpy(&addr4, addr, sizeof(addr4));
+       s_addr = ntohl(addr4.sin_addr.s_addr);
 
-       return 1;
+       memset(&addr6, 0, sizeof(addr6));
+
+       addr6.sin6_family = AF_INET6;
+
+       for (i = 0; i < 12; i++)
+               addr6.sin6_addr.s6_addr[i] = 0xff;
+
+       addr6.sin6_addr.s6_addr[12] = (s_addr >> 24) & 0xFF;
+       addr6.sin6_addr.s6_addr[13] = (s_addr >> 16) & 0xFF;
+       addr6.sin6_addr.s6_addr[14] = (s_addr >> 8) & 0xFF;
+       addr6.sin6_addr.s6_addr[15] = (s_addr) & 0xFF;
+
+       memcpy(addr, &addr6, sizeof(addr6));
 }
 
-#endif
+#endif /* HAVE_IPV6 */
index e5dba788c66733212a3d17f12d94a2a16bcc633f..53c65cc81ae9743930e6d30f207f3a0d31159790 100644 (file)
@@ -53,6 +53,5 @@
 local   all         all                                             trust
 host    all         all         127.0.0.1         255.255.255.255   trust
 
-# uncomment these to support IPv6 localhost connections
+# uncomment this to support IPv6 loopback connections
 # host  all         all         ::1               ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff        trust
-# host  all         all         ::ffff:127.0.0.1/128                trust
index c60030ccf50f951a2a4ea517fad4755271f06ad6..9858faaddc9888b8cb1fbc3930b807c37952f83b 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 2003, PostgreSQL Global Development Group
  *
- * $Id: ip.h,v 1.10 2003/08/04 00:43:31 momjian Exp $
+ * $Id: ip.h,v 1.11 2003/09/05 20:31:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,6 +33,11 @@ extern int rangeSockAddr(const struct sockaddr_storage * addr,
 extern int SockAddr_cidr_mask(struct sockaddr_storage ** mask,
                                   char *numbits, int family);
 
+#ifdef HAVE_IPV6
+extern void promote_v4_to_v6_addr(struct sockaddr_storage * addr);
+extern void promote_v4_to_v6_mask(struct sockaddr_storage * addr);
+#endif
+
 #ifdef HAVE_UNIX_SOCKETS
 #define IS_AF_UNIX(fam) ((fam) == AF_UNIX)
 #else