Skip to content

Commit 3c9bb88

Browse files
committed
Allow IPv4-format entries in pg_hba.conf to match IPv6 connections
that have IPv4-embedded-in-IPv6 addresses. Per idea of Andreas Pflug.
1 parent 23d07fa commit 3c9bb88

File tree

5 files changed

+151
-46
lines changed

5 files changed

+151
-46
lines changed

doc/src/sgml/client-auth.sgml

+16-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.56 2003/08/31 17:32:18 petere Exp $
2+
$Header: /cvsroot/pgsql/doc/src/sgml/client-auth.sgml,v 1.57 2003/09/05 20:31:35 tgl Exp $
33
-->
44

55
<chapter id="client-authentication">
@@ -199,13 +199,17 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable>
199199
<programlisting>
200200
(<replaceable>actual-IP-address</replaceable> xor <replaceable>IP-address-field</replaceable>) and <replaceable>IP-mask-field</replaceable>
201201
</programlisting>
202-
must be zero for the record to match. (Of course IP addresses
203-
can be spoofed but this consideration is beyond the scope of
204-
<productname>PostgreSQL</productname>.) If you machine supports
205-
IPv6, the default <filename>pg_hba.conf</> file will have an
206-
IPv6 entry for <literal>localhost</>. You can add your own IPv6
207-
entries to the file. IPv6 entries are used only for IPv6
208-
connections.
202+
must be zero for the record to match.
203+
</para>
204+
205+
<para>
206+
An IP address given in IPv4 format will match IPv6 connections that
207+
have the corresponding address, for example <literal>127.0.0.1</>
208+
will match the IPv6 address <literal>::ffff:127.0.0.1</>. An entry
209+
given in IPv6 format will match only IPv6 connections, even if the
210+
represented address is in the IPv4-in-IPv6 range. Note that entries
211+
in IPv6 format will be rejected if the system's C library does not have
212+
support for IPv6 addresses.
209213
</para>
210214

211215
<para>
@@ -219,9 +223,10 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable>
219223
<term><replaceable>CIDR-mask</replaceable></term>
220224
<listitem>
221225
<para>
222-
This is an integer specifying the number of significant bits
223-
to set in the mask, and is an alternative to using the
224-
<replaceable>IP-mask</replaceable> notation. The number must
226+
This field may be used as an alternative to the
227+
<replaceable>IP-mask</replaceable> notation. It is an
228+
integer specifying the number of high-order bits
229+
to set in the mask. The number must
225230
be between 0 and 32 (in the case of an IPv4 address) or 128
226231
(in the case of an IPv6 address) inclusive. 0 will match any
227232
address, while 32/128 will match only the exact host specified.

src/backend/libpq/hba.c

+23-8
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*
1111
*
1212
* IDENTIFICATION
13-
* $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.112 2003/09/05 03:57:13 momjian Exp $
13+
* $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.113 2003/09/05 20:31:35 tgl Exp $
1414
*
1515
*-------------------------------------------------------------------------
1616
*/
@@ -673,13 +673,6 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
673673
if (cidr_slash)
674674
*cidr_slash = '/';
675675

676-
if (file_ip_addr->ai_family != port->raddr.addr.ss_family)
677-
{
678-
/* Wrong address family. */
679-
freeaddrinfo_all(hints.ai_family, file_ip_addr);
680-
return;
681-
}
682-
683676
/* Get the netmask */
684677
if (cidr_slash)
685678
{
@@ -705,6 +698,28 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
705698
goto hba_syntax;
706699
}
707700

701+
if (file_ip_addr->ai_family != port->raddr.addr.ss_family)
702+
{
703+
/*
704+
* Wrong address family. We allow only one case: if the
705+
* file has IPv4 and the port is IPv6, promote the file
706+
* address to IPv6 and try to match that way.
707+
*/
708+
#ifdef HAVE_IPV6
709+
if (file_ip_addr->ai_family == AF_INET &&
710+
port->raddr.addr.ss_family == AF_INET6)
711+
{
712+
promote_v4_to_v6_addr((struct sockaddr_storage *) file_ip_addr->ai_addr);
713+
promote_v4_to_v6_mask(mask);
714+
}
715+
else
716+
#endif /* HAVE_IPV6 */
717+
{
718+
freeaddrinfo_all(hints.ai_family, file_ip_addr);
719+
return;
720+
}
721+
}
722+
708723
/* Read the rest of the line. */
709724
line = lnext(line);
710725
if (!line)

src/backend/libpq/ip.c

+105-24
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/libpq/ip.c,v 1.19 2003/08/04 02:39:59 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/libpq/ip.c,v 1.20 2003/09/05 20:31:36 tgl Exp $
1212
*
1313
* This file and the IPV6 implementation were initially provided by
1414
* Nigel Kukard <nkukard@lbsd.net>, Linux Based Systems Design
@@ -34,7 +34,8 @@
3434
#endif
3535
#include <arpa/inet.h>
3636
#include <sys/file.h>
37-
#endif
37+
38+
#endif /* !defined(_MSC_VER) && !defined(__BORLANDC__) */
3839

3940
#include "libpq/ip.h"
4041

@@ -265,9 +266,16 @@ getnameinfo_unix(const struct sockaddr_un * sa, int salen,
265266

266267
return 0;
267268
}
269+
268270
#endif /* HAVE_UNIX_SOCKETS */
269271

270272

273+
/*
274+
* rangeSockAddr - is addr within the subnet specified by netaddr/netmask ?
275+
*
276+
* Note: caller must already have verified that all three addresses are
277+
* in the same address family; and AF_UNIX addresses are not supported.
278+
*/
271279
int
272280
rangeSockAddr(const struct sockaddr_storage * addr,
273281
const struct sockaddr_storage * netaddr,
@@ -287,6 +295,39 @@ rangeSockAddr(const struct sockaddr_storage * addr,
287295
return 0;
288296
}
289297

298+
static int
299+
rangeSockAddrAF_INET(const struct sockaddr_in * addr,
300+
const struct sockaddr_in * netaddr,
301+
const struct sockaddr_in * netmask)
302+
{
303+
if (((addr->sin_addr.s_addr ^ netaddr->sin_addr.s_addr) &
304+
netmask->sin_addr.s_addr) == 0)
305+
return 1;
306+
else
307+
return 0;
308+
}
309+
310+
311+
#ifdef HAVE_IPV6
312+
static int
313+
rangeSockAddrAF_INET6(const struct sockaddr_in6 * addr,
314+
const struct sockaddr_in6 * netaddr,
315+
const struct sockaddr_in6 * netmask)
316+
{
317+
int i;
318+
319+
for (i = 0; i < 16; i++)
320+
{
321+
if (((addr->sin6_addr.s6_addr[i] ^ netaddr->sin6_addr.s6_addr[i]) &
322+
netmask->sin6_addr.s6_addr[i]) != 0)
323+
return 0;
324+
}
325+
326+
return 1;
327+
}
328+
329+
#endif
330+
290331
/*
291332
* SockAddr_cidr_mask - make a network mask of the appropriate family
292333
* and required number of significant bits
@@ -358,34 +399,74 @@ SockAddr_cidr_mask(struct sockaddr_storage ** mask, char *numbits, int family)
358399
return 0;
359400
}
360401

361-
static int
362-
rangeSockAddrAF_INET(const struct sockaddr_in * addr, const struct sockaddr_in * netaddr,
363-
const struct sockaddr_in * netmask)
402+
403+
#ifdef HAVE_IPV6
404+
405+
/*
406+
* promote_v4_to_v6_addr --- convert an AF_INET addr to AF_INET6, using
407+
* the standard convention for IPv4 addresses mapped into IPv6 world
408+
*
409+
* The passed addr is modified in place. Note that we only worry about
410+
* setting the fields that rangeSockAddr will look at.
411+
*/
412+
void
413+
promote_v4_to_v6_addr(struct sockaddr_storage * addr)
364414
{
365-
if (((addr->sin_addr.s_addr ^ netaddr->sin_addr.s_addr) &
366-
netmask->sin_addr.s_addr) == 0)
367-
return 1;
368-
else
369-
return 0;
370-
}
415+
struct sockaddr_in addr4;
416+
struct sockaddr_in6 addr6;
417+
uint32 s_addr;
371418

419+
memcpy(&addr4, addr, sizeof(addr4));
420+
s_addr = ntohl(addr4.sin_addr.s_addr);
372421

373-
#ifdef HAVE_IPV6
374-
static int
375-
rangeSockAddrAF_INET6(const struct sockaddr_in6 * addr,
376-
const struct sockaddr_in6 * netaddr,
377-
const struct sockaddr_in6 * netmask)
422+
memset(&addr6, 0, sizeof(addr6));
423+
424+
addr6.sin6_family = AF_INET6;
425+
426+
addr6.sin6_addr.s6_addr[10] = 0xff;
427+
addr6.sin6_addr.s6_addr[11] = 0xff;
428+
addr6.sin6_addr.s6_addr[12] = (s_addr >> 24) & 0xFF;
429+
addr6.sin6_addr.s6_addr[13] = (s_addr >> 16) & 0xFF;
430+
addr6.sin6_addr.s6_addr[14] = (s_addr >> 8) & 0xFF;
431+
addr6.sin6_addr.s6_addr[15] = (s_addr) & 0xFF;
432+
433+
memcpy(addr, &addr6, sizeof(addr6));
434+
}
435+
436+
/*
437+
* promote_v4_to_v6_mask --- convert an AF_INET netmask to AF_INET6, using
438+
* the standard convention for IPv4 addresses mapped into IPv6 world
439+
*
440+
* This must be different from promote_v4_to_v6_addr because we want to
441+
* set the high-order bits to 1's not 0's.
442+
*
443+
* The passed addr is modified in place. Note that we only worry about
444+
* setting the fields that rangeSockAddr will look at.
445+
*/
446+
void
447+
promote_v4_to_v6_mask(struct sockaddr_storage * addr)
378448
{
449+
struct sockaddr_in addr4;
450+
struct sockaddr_in6 addr6;
451+
uint32 s_addr;
379452
int i;
380453

381-
for (i = 0; i < 16; i++)
382-
{
383-
if (((addr->sin6_addr.s6_addr[i] ^ netaddr->sin6_addr.s6_addr[i]) &
384-
netmask->sin6_addr.s6_addr[i]) != 0)
385-
return 0;
386-
}
454+
memcpy(&addr4, addr, sizeof(addr4));
455+
s_addr = ntohl(addr4.sin_addr.s_addr);
387456

388-
return 1;
457+
memset(&addr6, 0, sizeof(addr6));
458+
459+
addr6.sin6_family = AF_INET6;
460+
461+
for (i = 0; i < 12; i++)
462+
addr6.sin6_addr.s6_addr[i] = 0xff;
463+
464+
addr6.sin6_addr.s6_addr[12] = (s_addr >> 24) & 0xFF;
465+
addr6.sin6_addr.s6_addr[13] = (s_addr >> 16) & 0xFF;
466+
addr6.sin6_addr.s6_addr[14] = (s_addr >> 8) & 0xFF;
467+
addr6.sin6_addr.s6_addr[15] = (s_addr) & 0xFF;
468+
469+
memcpy(addr, &addr6, sizeof(addr6));
389470
}
390471

391-
#endif
472+
#endif /* HAVE_IPV6 */

src/backend/libpq/pg_hba.conf.sample

+1-2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,5 @@
5353
local all all trust
5454
host all all 127.0.0.1 255.255.255.255 trust
5555

56-
# uncomment these to support IPv6 localhost connections
56+
# uncomment this to support IPv6 loopback connections
5757
# host all all ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff trust
58-
# host all all ::ffff:127.0.0.1/128 trust

src/include/libpq/ip.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*
66
* Copyright (c) 2003, PostgreSQL Global Development Group
77
*
8-
* $Id: ip.h,v 1.10 2003/08/04 00:43:31 momjian Exp $
8+
* $Id: ip.h,v 1.11 2003/09/05 20:31:36 tgl Exp $
99
*
1010
*-------------------------------------------------------------------------
1111
*/
@@ -33,6 +33,11 @@ extern int rangeSockAddr(const struct sockaddr_storage * addr,
3333
extern int SockAddr_cidr_mask(struct sockaddr_storage ** mask,
3434
char *numbits, int family);
3535

36+
#ifdef HAVE_IPV6
37+
extern void promote_v4_to_v6_addr(struct sockaddr_storage * addr);
38+
extern void promote_v4_to_v6_mask(struct sockaddr_storage * addr);
39+
#endif
40+
3641
#ifdef HAVE_UNIX_SOCKETS
3742
#define IS_AF_UNIX(fam) ((fam) == AF_UNIX)
3843
#else

0 commit comments

Comments
 (0)