Allow IPv4-format entries in pg_hba.conf to match IPv6 connections

that have IPv4-embedded-in-IPv6 addresses.  Per idea of Andreas Pflug.
This commit is contained in:
Tom Lane 2003-09-05 20:31:36 +00:00
parent 23d07fa357
commit 3c9bb8886d
5 changed files with 153 additions and 48 deletions

View 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"> <chapter id="client-authentication">
@ -199,13 +199,17 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable>
<programlisting> <programlisting>
(<replaceable>actual-IP-address</replaceable> xor <replaceable>IP-address-field</replaceable>) and <replaceable>IP-mask-field</replaceable> (<replaceable>actual-IP-address</replaceable> xor <replaceable>IP-address-field</replaceable>) and <replaceable>IP-mask-field</replaceable>
</programlisting> </programlisting>
must be zero for the record to match. (Of course IP addresses must be zero for the record to match.
can be spoofed but this consideration is beyond the scope of </para>
<productname>PostgreSQL</productname>.) If you machine supports
IPv6, the default <filename>pg_hba.conf</> file will have an <para>
IPv6 entry for <literal>localhost</>. You can add your own IPv6 An IP address given in IPv4 format will match IPv6 connections that
entries to the file. IPv6 entries are used only for IPv6 have the corresponding address, for example <literal>127.0.0.1</>
connections. 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>
<para> <para>
@ -219,9 +223,10 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable>
<term><replaceable>CIDR-mask</replaceable></term> <term><replaceable>CIDR-mask</replaceable></term>
<listitem> <listitem>
<para> <para>
This is an integer specifying the number of significant bits This field may be used as an alternative to the
to set in the mask, and is an alternative to using the <replaceable>IP-mask</replaceable> notation. It is an
<replaceable>IP-mask</replaceable> notation. The number must 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 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 (in the case of an IPv6 address) inclusive. 0 will match any
address, while 32/128 will match only the exact host specified. address, while 32/128 will match only the exact host specified.

View File

@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * 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) if (cidr_slash)
*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 */ /* Get the netmask */
if (cidr_slash) if (cidr_slash)
{ {
@ -705,6 +698,28 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
goto hba_syntax; 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. */ /* Read the rest of the line. */
line = lnext(line); line = lnext(line);
if (!line) if (!line)

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * This file and the IPV6 implementation were initially provided by
* Nigel Kukard <nkukard@lbsd.net>, Linux Based Systems Design * Nigel Kukard <nkukard@lbsd.net>, Linux Based Systems Design
@ -34,7 +34,8 @@
#endif #endif
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/file.h> #include <sys/file.h>
#endif
#endif /* !defined(_MSC_VER) && !defined(__BORLANDC__) */
#include "libpq/ip.h" #include "libpq/ip.h"
@ -265,9 +266,16 @@ getnameinfo_unix(const struct sockaddr_un * sa, int salen,
return 0; return 0;
} }
#endif /* HAVE_UNIX_SOCKETS */ #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 int
rangeSockAddr(const struct sockaddr_storage * addr, rangeSockAddr(const struct sockaddr_storage * addr,
const struct sockaddr_storage * netaddr, const struct sockaddr_storage * netaddr,
@ -287,6 +295,39 @@ rangeSockAddr(const struct sockaddr_storage * addr,
return 0; 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 * SockAddr_cidr_mask - make a network mask of the appropriate family
* and required number of significant bits * and required number of significant bits
@ -358,34 +399,74 @@ SockAddr_cidr_mask(struct sockaddr_storage ** mask, char *numbits, int family)
return 0; 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 #ifdef HAVE_IPV6
static int
rangeSockAddrAF_INET6(const struct sockaddr_in6 * addr, /*
const struct sockaddr_in6 * netaddr, * promote_v4_to_v6_addr --- convert an AF_INET addr to AF_INET6, using
const struct sockaddr_in6 * netmask) * 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)
{ {
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
uint32 s_addr;
memcpy(&addr4, addr, sizeof(addr4));
s_addr = ntohl(addr4.sin_addr.s_addr);
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; int i;
for (i = 0; i < 16; i++) memcpy(&addr4, addr, sizeof(addr4));
{ s_addr = ntohl(addr4.sin_addr.s_addr);
if (((addr->sin6_addr.s6_addr[i] ^ netaddr->sin6_addr.s6_addr[i]) &
netmask->sin6_addr.s6_addr[i]) != 0) memset(&addr6, 0, sizeof(addr6));
return 0;
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));
} }
return 1; #endif /* HAVE_IPV6 */
}
#endif

View File

@ -53,6 +53,5 @@
local all all trust local all all trust
host all all 127.0.0.1 255.255.255.255 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 ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff trust
# host all all ::ffff:127.0.0.1/128 trust

View File

@ -5,7 +5,7 @@
* *
* Copyright (c) 2003, PostgreSQL Global Development Group * 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, extern int SockAddr_cidr_mask(struct sockaddr_storage ** mask,
char *numbits, int family); 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 #ifdef HAVE_UNIX_SOCKETS
#define IS_AF_UNIX(fam) ((fam) == AF_UNIX) #define IS_AF_UNIX(fam) ((fam) == AF_UNIX)
#else #else