Support "samehost" and "samenet" specifications in pg_hba.conf,

by enumerating the machine's IP interfaces to look for a match.

Stef Walter
This commit is contained in:
Tom Lane 2009-10-01 01:58:58 +00:00
parent f7082f269e
commit f3aec2c7f5
13 changed files with 867 additions and 115 deletions

77
configure vendored
View File

@ -9691,7 +9691,10 @@ done
for ac_header in crypt.h dld.h fp_class.h getopt.h ieeefp.h langinfo.h poll.h pwd.h sys/ipc.h sys/poll.h sys/pstat.h sys/resource.h sys/select.h sys/sem.h sys/socket.h sys/shm.h sys/tas.h sys/time.h sys/un.h termios.h ucred.h utime.h wchar.h wctype.h kernel/OS.h kernel/image.h SupportDefs.h
for ac_header in crypt.h dld.h fp_class.h getopt.h ieeefp.h ifaddrs.h langinfo.h poll.h pwd.h sys/ioctl.h sys/ipc.h sys/poll.h sys/pstat.h sys/resource.h sys/select.h sys/sem.h sys/shm.h sys/socket.h sys/sockio.h sys/tas.h sys/time.h sys/un.h termios.h ucred.h utime.h wchar.h wctype.h kernel/OS.h kernel/image.h SupportDefs.h
do
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
@ -9842,6 +9845,75 @@ fi
done
# On BSD, cpp test for net/if.h will fail unless sys/socket.h
# is included first.
for ac_header in net/if.h
do
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
$as_echo_n "checking for $ac_header... " >&6; }
if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
$as_echo_n "(cached) " >&6
else
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
$ac_includes_default
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include <$ac_header>
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
$as_echo "$ac_try_echo") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
$as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
eval "$as_ac_Header=yes"
else
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
eval "$as_ac_Header=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
ac_res=`eval 'as_val=${'$as_ac_Header'}
$as_echo "$as_val"'`
{ $as_echo "$as_me:$LINENO: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
as_val=`eval 'as_val=${'$as_ac_Header'}
$as_echo "$as_val"'`
if test "x$as_val" = x""yes; then
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
fi
done
# At least on IRIX, cpp test for netinet/tcp.h will fail unless
# netinet/in.h is included first.
@ -17327,7 +17399,8 @@ fi
for ac_func in cbrt dlopen fcvt fdatasync getpeereid getpeerucred getrlimit memmove poll pstat readlink setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs
for ac_func in cbrt dlopen fcvt fdatasync getifaddrs getpeereid getpeerucred getrlimit memmove poll pstat readlink setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs
do
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5

View File

@ -1,5 +1,5 @@
dnl Process this file with autoconf to produce a configure script.
dnl $PostgreSQL: pgsql/configure.in,v 1.611 2009/09/13 22:18:22 tgl Exp $
dnl $PostgreSQL: pgsql/configure.in,v 1.612 2009/10/01 01:58:57 tgl Exp $
dnl
dnl Developers, please strive to achieve this order:
dnl
@ -969,7 +969,16 @@ AC_SUBST(OSSP_UUID_LIBS)
##
dnl sys/socket.h is required by AC_FUNC_ACCEPT_ARGTYPES
AC_CHECK_HEADERS([crypt.h dld.h fp_class.h getopt.h ieeefp.h langinfo.h poll.h pwd.h sys/ipc.h sys/poll.h sys/pstat.h sys/resource.h sys/select.h sys/sem.h sys/socket.h sys/shm.h sys/tas.h sys/time.h sys/un.h termios.h ucred.h utime.h wchar.h wctype.h kernel/OS.h kernel/image.h SupportDefs.h])
AC_CHECK_HEADERS([crypt.h dld.h fp_class.h getopt.h ieeefp.h ifaddrs.h langinfo.h poll.h pwd.h sys/ioctl.h sys/ipc.h sys/poll.h sys/pstat.h sys/resource.h sys/select.h sys/sem.h sys/shm.h sys/socket.h sys/sockio.h sys/tas.h sys/time.h sys/un.h termios.h ucred.h utime.h wchar.h wctype.h kernel/OS.h kernel/image.h SupportDefs.h])
# On BSD, cpp test for net/if.h will fail unless sys/socket.h
# is included first.
AC_CHECK_HEADERS(net/if.h, [], [],
[AC_INCLUDES_DEFAULT
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
])
# At least on IRIX, cpp test for netinet/tcp.h will fail unless
# netinet/in.h is included first.
@ -1148,7 +1157,7 @@ PGAC_VAR_INT_TIMEZONE
AC_FUNC_ACCEPT_ARGTYPES
PGAC_FUNC_GETTIMEOFDAY_1ARG
AC_CHECK_FUNCS([cbrt dlopen fcvt fdatasync getpeereid getpeerucred getrlimit memmove poll pstat readlink setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs])
AC_CHECK_FUNCS([cbrt dlopen fcvt fdatasync getifaddrs getpeereid getpeerucred getrlimit memmove poll pstat readlink setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs])
# posix_fadvise() is a no-op on Solaris, so don't incur function overhead
# by calling it, 2009-04-02

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.123 2009/06/24 13:46:32 mha Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.124 2009/10/01 01:58:57 tgl Exp $ -->
<chapter id="client-authentication">
<title>Client Authentication</title>
@ -225,6 +225,13 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable>
<literal>/</literal>, and the CIDR mask length.
</para>
<para>
Instead of a <replaceable>CIDR-address</replaceable>, you can write
<literal>samehost</literal> to match any of the server's own IP
addresses, or <literal>samenet</literal> to match any address in any
subnet that the server is directly connected to.
</para>
<para>
Typical examples of a <replaceable>CIDR-address</replaceable> are
<literal>172.20.143.89/32</literal> for a single host, or

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.190 2009/09/01 02:54:51 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.191 2009/10/01 01:58:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -42,6 +42,14 @@
#define MAX_TOKEN 256
/* callback data for check_network_callback */
typedef struct check_network_data
{
IPCompareMethod method; /* test method */
SockAddr *raddr; /* client's actual address */
bool result; /* set to true if match */
} check_network_data;
/* pre-parsed content of HBA config file: list of HbaLine structs */
static List *parsed_hba_lines = NIL;
@ -512,6 +520,99 @@ check_db(const char *dbname, const char *role, Oid roleid, char *param_str)
return false;
}
/*
* Check to see if a connecting IP matches the given address and netmask.
*/
static bool
check_ip(SockAddr *raddr, struct sockaddr *addr, struct sockaddr *mask)
{
if (raddr->addr.ss_family == addr->sa_family)
{
/* Same address family */
if (!pg_range_sockaddr(&raddr->addr,
(struct sockaddr_storage*)addr,
(struct sockaddr_storage*)mask))
return false;
}
#ifdef HAVE_IPV6
else if (addr->sa_family == AF_INET &&
raddr->addr.ss_family == AF_INET6)
{
/*
* If we're connected on IPv6 but the file specifies an IPv4 address
* to match against, promote the latter to an IPv6 address
* before trying to match the client's address.
*/
struct sockaddr_storage addrcopy,
maskcopy;
memcpy(&addrcopy, &addr, sizeof(addrcopy));
memcpy(&maskcopy, &mask, sizeof(maskcopy));
pg_promote_v4_to_v6_addr(&addrcopy);
pg_promote_v4_to_v6_mask(&maskcopy);
if (!pg_range_sockaddr(&raddr->addr, &addrcopy, &maskcopy))
return false;
}
#endif /* HAVE_IPV6 */
else
{
/* Wrong address family, no IPV6 */
return false;
}
return true;
}
/*
* pg_foreach_ifaddr callback: does client addr match this machine interface?
*/
static void
check_network_callback(struct sockaddr *addr, struct sockaddr *netmask,
void *cb_data)
{
check_network_data *cn = (check_network_data *) cb_data;
struct sockaddr_storage mask;
/* Already found a match? */
if (cn->result)
return;
if (cn->method == ipCmpSameHost)
{
/* Make an all-ones netmask of appropriate length for family */
pg_sockaddr_cidr_mask(&mask, NULL, addr->sa_family);
cn->result = check_ip(cn->raddr, addr, (struct sockaddr*) &mask);
}
else
{
/* Use the netmask of the interface itself */
cn->result = check_ip(cn->raddr, addr, netmask);
}
}
/*
* Use pg_foreach_ifaddr to check a samehost or samenet match
*/
static bool
check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
{
check_network_data cn;
cn.method = method;
cn.raddr = raddr;
cn.result = false;
errno = 0;
if (pg_foreach_ifaddr(check_network_callback, &cn) < 0)
{
elog(LOG, "error enumerating network interfaces: %m");
return false;
}
return cn.result;
}
/*
* Macros used to check and report on invalid configuration options.
@ -658,99 +759,121 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
line_num, HbaFileName)));
return false;
}
token = pstrdup(lfirst(line_item));
token = lfirst(line_item);
/* Check if it has a CIDR suffix and if so isolate it */
cidr_slash = strchr(token, '/');
if (cidr_slash)
*cidr_slash = '\0';
/* Get the IP address either way */
hints.ai_flags = AI_NUMERICHOST;
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = 0;
hints.ai_protocol = 0;
hints.ai_addrlen = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
if (ret || !gai_result)
/* Is it equal to 'samehost' or 'samenet'? */
if (strcmp(token, "samehost") == 0)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid IP address \"%s\": %s",
token, gai_strerror(ret)),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
if (cidr_slash)
*cidr_slash = '/';
if (gai_result)
pg_freeaddrinfo_all(hints.ai_family, gai_result);
return false;
/* Any IP on this host is allowed to connect */
parsedline->ip_cmp_method = ipCmpSameHost;
}
if (cidr_slash)
*cidr_slash = '/';
memcpy(&parsedline->addr, gai_result->ai_addr, gai_result->ai_addrlen);
pg_freeaddrinfo_all(hints.ai_family, gai_result);
/* Get the netmask */
if (cidr_slash)
else if (strcmp(token, "samenet") == 0)
{
if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
parsedline->addr.ss_family) < 0)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid CIDR mask in address \"%s\"",
token),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
return false;
}
/* Any IP on the host's subnets is allowed to connect */
parsedline->ip_cmp_method = ipCmpSameNet;
}
else
{
/* Read the mask field. */
line_item = lnext(line_item);
if (!line_item)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("end-of-line before netmask specification"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
return false;
}
token = lfirst(line_item);
/* IP and netmask are specified */
parsedline->ip_cmp_method = ipCmpMask;
/* need a modifiable copy of token */
token = pstrdup(token);
/* Check if it has a CIDR suffix and if so isolate it */
cidr_slash = strchr(token, '/');
if (cidr_slash)
*cidr_slash = '\0';
/* Get the IP address either way */
hints.ai_flags = AI_NUMERICHOST;
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = 0;
hints.ai_protocol = 0;
hints.ai_addrlen = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
if (ret || !gai_result)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid IP mask \"%s\": %s",
errmsg("invalid IP address \"%s\": %s",
token, gai_strerror(ret)),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
if (cidr_slash)
*cidr_slash = '/';
if (gai_result)
pg_freeaddrinfo_all(hints.ai_family, gai_result);
return false;
}
memcpy(&parsedline->mask, gai_result->ai_addr, gai_result->ai_addrlen);
if (cidr_slash)
*cidr_slash = '/';
memcpy(&parsedline->addr, gai_result->ai_addr,
gai_result->ai_addrlen);
pg_freeaddrinfo_all(hints.ai_family, gai_result);
if (parsedline->addr.ss_family != parsedline->mask.ss_family)
/* Get the netmask */
if (cidr_slash)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("IP address and mask do not match in file \"%s\" line %d",
HbaFileName, line_num)));
return false;
if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
parsedline->addr.ss_family) < 0)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid CIDR mask in address \"%s\"",
token),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
return false;
}
}
else
{
/* Read the mask field. */
line_item = lnext(line_item);
if (!line_item)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("end-of-line before netmask specification"),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
return false;
}
token = lfirst(line_item);
ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
if (ret || !gai_result)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("invalid IP mask \"%s\": %s",
token, gai_strerror(ret)),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
if (gai_result)
pg_freeaddrinfo_all(hints.ai_family, gai_result);
return false;
}
memcpy(&parsedline->mask, gai_result->ai_addr,
gai_result->ai_addrlen);
pg_freeaddrinfo_all(hints.ai_family, gai_result);
if (parsedline->addr.ss_family != parsedline->mask.ss_family)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("IP address and mask do not match in file \"%s\" line %d",
HbaFileName, line_num)));
return false;
}
}
}
} /* != ctLocal */
@ -1097,35 +1220,24 @@ check_hba(hbaPort *port)
#endif
/* Check IP address */
if (port->raddr.addr.ss_family == hba->addr.ss_family)
switch (hba->ip_cmp_method)
{
if (!pg_range_sockaddr(&port->raddr.addr, &hba->addr, &hba->mask))
case ipCmpMask:
if (!check_ip(&port->raddr,
(struct sockaddr *) &hba->addr,
(struct sockaddr *) &hba->mask))
continue;
break;
case ipCmpSameHost:
case ipCmpSameNet:
if (!check_same_host_or_net(&port->raddr,
hba->ip_cmp_method))
continue;
break;
default:
/* shouldn't get here, but deem it no-match if so */
continue;
}
#ifdef HAVE_IPV6
else if (hba->addr.ss_family == AF_INET &&
port->raddr.addr.ss_family == AF_INET6)
{
/*
* 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.
*/
struct sockaddr_storage addrcopy,
maskcopy;
memcpy(&addrcopy, &hba->addr, sizeof(addrcopy));
memcpy(&maskcopy, &hba->mask, sizeof(maskcopy));
pg_promote_v4_to_v6_addr(&addrcopy);
pg_promote_v4_to_v6_mask(&maskcopy);
if (!pg_range_sockaddr(&port->raddr.addr, &addrcopy, &maskcopy))
continue;
}
#endif /* HAVE_IPV6 */
else
/* Wrong address family, no IPV6 */
continue;
} /* != ctLocal */
/* Check database and role */

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/libpq/ip.c,v 1.47 2009/06/11 19:00:15 tgl Exp $
* $PostgreSQL: pgsql/src/backend/libpq/ip.c,v 1.48 2009/10/01 01:58:57 tgl Exp $
*
* This file and the IPV6 implementation were initially provided by
* Nigel Kukard <nkukard@lbsd.net>, Linux Based Systems Design
@ -333,6 +333,8 @@ range_sockaddr_AF_INET6(const struct sockaddr_in6 * addr,
* pg_sockaddr_cidr_mask - make a network mask of the appropriate family
* and required number of significant bits
*
* numbits can be null, in which case the mask is fully set.
*
* The resulting mask is placed in *mask, which had better be big enough.
*
* Return value is 0 if okay, -1 if not.
@ -343,10 +345,16 @@ pg_sockaddr_cidr_mask(struct sockaddr_storage * mask, char *numbits, int family)
long bits;
char *endptr;
bits = strtol(numbits, &endptr, 10);
if (*numbits == '\0' || *endptr != '\0')
return -1;
if (numbits == NULL)
{
bits = (family == AF_INET) ? 32 : 128;
}
else
{
bits = strtol(numbits, &endptr, 10);
if (*numbits == '\0' || *endptr != '\0')
return -1;
}
switch (family)
{
@ -476,3 +484,401 @@ pg_promote_v4_to_v6_mask(struct sockaddr_storage * addr)
}
#endif /* HAVE_IPV6 */
/*
* Run the callback function for the addr/mask, after making sure the
* mask is sane for the addr.
*/
static void
run_ifaddr_callback(PgIfAddrCallback callback, void *cb_data,
struct sockaddr *addr, struct sockaddr *mask)
{
struct sockaddr_storage fullmask;
if (!addr)
return;
/* Check that the mask is valid */
if (mask)
{
if (mask->sa_family != addr->sa_family)
{
mask = NULL;
}
else if (mask->sa_family == AF_INET)
{
if (((struct sockaddr_in*)mask)->sin_addr.s_addr == INADDR_ANY)
mask = NULL;
}
#ifdef HAVE_IPV6
else if (mask->sa_family == AF_INET6)
{
if (IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6*)mask)->sin6_addr))
mask = NULL;
}
#endif
}
/* If mask is invalid, generate our own fully-set mask */
if (!mask)
{
pg_sockaddr_cidr_mask(&fullmask, NULL, addr->sa_family);
mask = (struct sockaddr*) &fullmask;
}
(*callback) (addr, mask, cb_data);
}
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
/*
* Enumerate the system's network interface addresses and call the callback
* for each one. Returns 0 if successful, -1 if trouble.
*
* This version is for Win32. Uses the Winsock 2 functions (ie: ws2_32.dll)
*/
int
pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
{
INTERFACE_INFO *ptr, *ii = NULL;
unsigned long length, i;
unsigned long n_ii = 0;
SOCKET sock;
int error;
sock = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
if (sock == SOCKET_ERROR)
return -1;
while (n_ii < 1024)
{
n_ii += 64;
ptr = realloc(ii, sizeof (INTERFACE_INFO) * n_ii);
if (!ptr)
{
free(ii);
closesocket(sock);
errno = ENOMEM;
return -1;
}
ii = ptr;
if (WSAIoctl(sock, SIO_GET_INTERFACE_LIST, 0, 0,
ii, n_ii * sizeof (INTERFACE_INFO),
&length, 0, 0) == SOCKET_ERROR)
{
error = WSAGetLastError();
if (error == WSAEFAULT || error == WSAENOBUFS)
continue; /* need to make the buffer bigger */
closesocket(sock);
free(ii);
return -1;
}
break;
}
for (i = 0; i < length / sizeof(INTERFACE_INFO); ++i)
run_ifaddr_callback(callback, cb_data,
(struct sockaddr*)&ii[i].iiAddress,
(struct sockaddr*)&ii[i].iiNetmask);
closesocket(sock);
free(ii);
return 0;
}
#elif HAVE_GETIFADDRS /* && !WIN32 */
#ifdef HAVE_IFADDRS_H
#include <ifaddrs.h>
#endif
/*
* Enumerate the system's network interface addresses and call the callback
* for each one. Returns 0 if successful, -1 if trouble.
*
* This version uses the getifaddrs() interface, which is available on
* BSDs, AIX, and modern Linux.
*/
int
pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
{
struct ifaddrs *ifa, *l;
if (getifaddrs(&ifa) < 0)
return -1;
for (l = ifa; l; l = l->ifa_next)
run_ifaddr_callback(callback, cb_data,
l->ifa_addr, l->ifa_netmask);
freeifaddrs(ifa);
return 0;
}
#else /* !HAVE_GETIFADDRS && !WIN32 */
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#ifdef HAVE_SYS_SOCKIO_H
#include <sys/sockio.h>
#endif
/*
* SIOCGIFCONF does not return IPv6 addresses on Solaris
* and HP/UX. So we prefer SIOCGLIFCONF if it's available.
*/
#if defined(SIOCGLIFCONF)
/*
* Enumerate the system's network interface addresses and call the callback
* for each one. Returns 0 if successful, -1 if trouble.
*
* This version uses ioctl(SIOCGLIFCONF).
*/
int
pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
{
struct lifconf lifc;
struct lifreq *lifr, lmask;
struct sockaddr *addr, *mask;
char *ptr, *buffer = NULL;
size_t n_buffer = 1024;
int sock, fd;
#ifdef HAVE_IPV6
int sock6;
#endif
int i, total;
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1)
return -1;
while (n_buffer < 1024 * 100)
{
n_buffer += 1024;
ptr = realloc(buffer, n_buffer);
if (!ptr)
{
free(buffer);
close(sock);
errno = ENOMEM;
return -1;
}
memset(&lifc, 0, sizeof (lifc));
lifc.lifc_family = AF_UNSPEC;
lifc.lifc_buf = buffer = ptr;
lifc.lifc_len = n_buffer;
if (ioctl(sock, SIOCGLIFCONF, &lifc) < 0)
{
if (errno == EINVAL)
continue;
free(buffer);
close(sock);
return -1;
}
/*
* Some Unixes try to return as much data as possible,
* with no indication of whether enough space allocated.
* Don't believe we have it all unless there's lots of slop.
*/
if (lifc.lifc_len < n_buffer - 1024)
break;
}
#ifdef HAVE_IPV6
/* We'll need an IPv6 socket too for the SIOCGLIFNETMASK ioctls */
sock6 = socket(AF_INET6, SOCK_DGRAM, 0);
if (sock6 == -1)
{
free(buffer);
close(sock);
return -1;
}
#endif
total = lifc.lifc_len / sizeof(struct lifreq);
lifr = lifc.lifc_req;
for (i = 0; i < total; ++i)
{
addr = (struct sockaddr*)&lifr[i].lifr_addr;
memcpy(&lmask, &lifr[i], sizeof(struct lifreq));
#ifdef HAVE_IPV6
fd = (addr->sa_family == AF_INET6) ? sock6 : sock;
#else
fd = sock;
#endif
if (ioctl(fd, SIOCGLIFNETMASK, &lmask) < 0)
mask = NULL;
else
mask = (struct sockaddr*)&lmask.lifr_addr;
run_ifaddr_callback(callback, cb_data, addr, mask);
}
free(buffer);
close(sock);
#ifdef HAVE_IPV6
close(sock6);
#endif
return 0;
}
#elif defined(SIOCGIFCONF)
/*
* Remaining Unixes use SIOCGIFCONF. Some only return IPv4 information
* here, so this is the least preferred method. Note that there is no
* standard way to iterate the struct ifreq returned in the array.
* On some OSs the structures are padded large enough for any address,
* on others you have to calculate the size of the struct ifreq.
*/
/* Some OSs have _SIZEOF_ADDR_IFREQ, so just use that */
#ifndef _SIZEOF_ADDR_IFREQ
/* Calculate based on sockaddr.sa_len */
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
#define _SIZEOF_ADDR_IFREQ(ifr) \
((ifr).ifr_addr.sa_len > sizeof(struct sockaddr) ? \
(sizeof(struct ifreq) - sizeof(struct sockaddr) + \
(ifr).ifr_addr.sa_len) : sizeof(struct ifreq))
/* Padded ifreq structure, simple */
#else
#define _SIZEOF_ADDR_IFREQ(ifr) \
sizeof (struct ifreq)
#endif
#endif /* !_SIZEOF_ADDR_IFREQ */
/*
* Enumerate the system's network interface addresses and call the callback
* for each one. Returns 0 if successful, -1 if trouble.
*
* This version uses ioctl(SIOCGIFCONF).
*/
int
pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
{
struct ifconf ifc;
struct ifreq *ifr, *end, addr, mask;
char *ptr, *buffer = NULL;
size_t n_buffer = 1024;
int sock;
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1)
return -1;
while (n_buffer < 1024 * 100)
{
n_buffer += 1024;
ptr = realloc(buffer, n_buffer);
if (!ptr)
{
free(buffer);
close(sock);
errno = ENOMEM;
return -1;
}
memset(&ifc, 0, sizeof (ifc));
ifc.ifc_buf = buffer = ptr;
ifc.ifc_len = n_buffer;
if (ioctl(sock, SIOCGIFCONF, &ifc) < 0)
{
if (errno == EINVAL)
continue;
free(buffer);
close(sock);
return -1;
}
/*
* Some Unixes try to return as much data as possible,
* with no indication of whether enough space allocated.
* Don't believe we have it all unless there's lots of slop.
*/
if (ifc.ifc_len < n_buffer - 1024)
break;
}
end = (struct ifreq*)(buffer + ifc.ifc_len);
for (ifr = ifc.ifc_req; ifr < end;)
{
memcpy(&addr, ifr, sizeof(addr));
memcpy(&mask, ifr, sizeof(mask));
if (ioctl(sock, SIOCGIFADDR, &addr, sizeof(addr)) == 0 &&
ioctl(sock, SIOCGIFNETMASK, &mask, sizeof(mask)) == 0)
run_ifaddr_callback(callback, cb_data,
&addr.ifr_addr, &mask.ifr_addr);
ifr = (struct ifreq*)((char*)ifr + _SIZEOF_ADDR_IFREQ(*ifr));
}
free(buffer);
close(sock);
return 0;
}
#else /* !defined(SIOCGIFCONF) */
/*
* Enumerate the system's network interface addresses and call the callback
* for each one. Returns 0 if successful, -1 if trouble.
*
* This version is our fallback if there's no known way to get the
* interface addresses. Just return the standard loopback addresses.
*/
int
pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
{
struct sockaddr_in addr;
struct sockaddr_storage mask;
#ifdef HAVE_IPV6
struct sockaddr_in6 addr6;
#endif
/* addr 127.0.0.1/8 */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = ntohl(0x7f000001);
memset(&mask, 0, sizeof(mask));
pg_sockaddr_cidr_mask(&mask, "8", AF_INET);
run_ifaddr_callback(callback, cb_data,
(struct sockaddr*)&addr,
(struct sockaddr*)&mask);
#ifdef HAVE_IPV6
/* addr ::1/128 */
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_addr.s6_addr[15] = 1;
memset(&mask, 0, sizeof(mask));
pg_sockaddr_cidr_mask(&mask, "128", AF_INET6);
run_ifaddr_callback(callback, cb_data,
(struct sockaddr*)&addr6,
(struct sockaddr*)&mask);
#endif
return 0;
}
#endif /* !defined(SIOCGIFCONF) */
#endif /* !HAVE_GETIFADDRS */

View File

@ -33,6 +33,9 @@
# (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that specifies
# the number of significant bits in the mask. Alternatively, you can write
# an IP address and netmask in separate columns to specify the set of hosts.
# Instead of a CIDR-address, you can write "samehost" to match any of the
# server's own IP addresses, or "samenet" to match any address in any subnet
# that the server is directly connected to.
#
# METHOD can be "trust", "reject", "md5", "password", "gss", "sspi", "krb5",
# "ident", "pam", "ldap" or "cert". Note that "password" sends passwords

View File

@ -4,7 +4,7 @@
* Interface to hba.c
*
*
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.58 2009/09/01 03:53:08 tgl Exp $
* $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.59 2009/10/01 01:58:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -30,6 +30,13 @@ typedef enum UserAuth
uaCert
} UserAuth;
typedef enum IPCompareMethod
{
ipCmpMask,
ipCmpSameHost,
ipCmpSameNet
} IPCompareMethod;
typedef enum ConnType
{
ctLocal,
@ -46,6 +53,7 @@ typedef struct
char *role;
struct sockaddr_storage addr;
struct sockaddr_storage mask;
IPCompareMethod ip_cmp_method;
UserAuth auth_method;
char *usermap;

View File

@ -8,7 +8,7 @@
*
* Copyright (c) 2003-2009, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/include/libpq/ip.h,v 1.21 2009/01/01 17:23:59 momjian Exp $
* $PostgreSQL: pgsql/src/include/libpq/ip.h,v 1.22 2009/10/01 01:58:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -19,6 +19,16 @@
#include "libpq/pqcomm.h"
#ifdef HAVE_UNIX_SOCKETS
#define IS_AF_UNIX(fam) ((fam) == AF_UNIX)
#else
#define IS_AF_UNIX(fam) (0)
#endif
typedef void (*PgIfAddrCallback) (struct sockaddr *addr,
struct sockaddr *netmask,
void *cb_data);
extern int pg_getaddrinfo_all(const char *hostname, const char *servname,
const struct addrinfo * hintp,
struct addrinfo ** result);
@ -41,10 +51,6 @@ extern void pg_promote_v4_to_v6_addr(struct sockaddr_storage * addr);
extern void pg_promote_v4_to_v6_mask(struct sockaddr_storage * addr);
#endif
#ifdef HAVE_UNIX_SOCKETS
#define IS_AF_UNIX(fam) ((fam) == AF_UNIX)
#else
#define IS_AF_UNIX(fam) (0)
#endif
extern int pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data);
#endif /* IP_H */

View File

@ -179,6 +179,9 @@
/* Define to 1 if you have the `gethostbyname_r' function. */
#undef HAVE_GETHOSTBYNAME_R
/* Define to 1 if you have the `getifaddrs' function. */
#undef HAVE_GETIFADDRS
/* Define to 1 if you have the `getopt' function. */
#undef HAVE_GETOPT
@ -221,6 +224,9 @@
/* Define to 1 if you have the <ieeefp.h> header file. */
#undef HAVE_IEEEFP_H
/* Define to 1 if you have the <ifaddrs.h> header file. */
#undef HAVE_IFADDRS_H
/* Define to 1 if you have the `inet_aton' function. */
#undef HAVE_INET_ATON
@ -336,6 +342,9 @@
/* Define to 1 if you have the <netinet/tcp.h> header file. */
#undef HAVE_NETINET_TCP_H
/* Define to 1 if you have the <net/if.h> header file. */
#undef HAVE_NET_IF_H
/* Define to 1 if you have the `on_exit' function. */
#undef HAVE_ON_EXIT
@ -523,6 +532,9 @@
/* Define to 1 if you have the syslog interface. */
#undef HAVE_SYSLOG
/* Define to 1 if you have the <sys/ioctl.h> header file. */
#undef HAVE_SYS_IOCTL_H
/* Define to 1 if you have the <sys/ipc.h> header file. */
#undef HAVE_SYS_IPC_H
@ -547,6 +559,9 @@
/* Define to 1 if you have the <sys/socket.h> header file. */
#undef HAVE_SYS_SOCKET_H
/* Define to 1 if you have the <sys/sockio.h> header file. */
#undef HAVE_SYS_SOCKIO_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H

View File

@ -0,0 +1,27 @@
#-------------------------------------------------------------------------
#
# Makefile for src/tools/ifaddrs
#
# Copyright (c) 2003-2009, PostgreSQL Global Development Group
#
# $PostgreSQL: pgsql/src/tools/ifaddrs/Makefile,v 1.1 2009/10/01 01:58:58 tgl Exp $
#
#-------------------------------------------------------------------------
subdir = src/tools/ifaddrs
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
libpq_backend_dir = $(top_builddir)/src/backend/libpq
override CPPFLAGS := -I$(libpq_backend_dir) $(CPPFLAGS)
OBJS = test_ifaddrs.o
all: test_ifaddrs
test_ifaddrs: test_ifaddrs.o $(libpq_backend_dir)/ip.o
$(CC) $(CFLAGS) test_ifaddrs.o $(libpq_backend_dir)/ip.o $(LDFLAGS) $(LIBS) -o $@$(X)
clean distclean maintainer-clean:
rm -f test_ifaddrs$(X) $(OBJS)

12
src/tools/ifaddrs/README Normal file
View File

@ -0,0 +1,12 @@
$PostgreSQL: pgsql/src/tools/ifaddrs/README,v 1.1 2009/10/01 01:58:58 tgl Exp $
test_ifaddrs
============
This program prints the addresses and netmasks of all the IPv4 and IPv6
interfaces on the local machine. It is useful for testing that this
functionality works on various platforms. If "samehost" and "samenet"
in pg_hba.conf don't seem to work right, run this program to see what
is happening.
Usage: test_ifaddrs

View File

@ -0,0 +1,73 @@
/*
* $PostgreSQL: pgsql/src/tools/ifaddrs/test_ifaddrs.c,v 1.1 2009/10/01 01:58:58 tgl Exp $
*
*
* test_ifaddrs.c
* test pg_foreach_ifaddr()
*/
#include "postgres.h"
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "libpq/ip.h"
static void
print_addr(struct sockaddr *addr)
{
char buffer[256];
int ret, len;
switch (addr->sa_family)
{
case AF_INET:
len = sizeof(struct sockaddr_in);
break;
#ifdef HAVE_IPV6
case AF_INET6:
len = sizeof(struct sockaddr_in6);
break;
#endif
default:
len = sizeof(struct sockaddr_storage);
break;
}
ret = getnameinfo(addr, len, buffer, sizeof(buffer), NULL, 0,
NI_NUMERICHOST);
if (ret != 0)
printf("[unknown: family %d]", addr->sa_family);
else
printf("%s", buffer);
}
static void
callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
{
printf("addr: ");
print_addr(addr);
printf(" mask: ");
print_addr(mask);
printf("\n");
}
int
main(int argc, char *argv[])
{
#ifdef WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
fprintf(stderr, "WSAStartup failed\n");
return 1;
}
#endif
if (pg_foreach_ifaddr(callback, NULL) < 0)
fprintf(stderr, "pg_foreach_ifaddr failed: %s\n", strerror(errno));
return 0;
}

View File

@ -3,7 +3,7 @@ package Mkvcbuild;
#
# Package that generates build files for msvc build
#
# $PostgreSQL: pgsql/src/tools/msvc/Mkvcbuild.pm,v 1.42 2009/08/07 20:50:22 petere Exp $
# $PostgreSQL: pgsql/src/tools/msvc/Mkvcbuild.pm,v 1.43 2009/10/01 01:58:58 tgl Exp $
#
use Carp;
use Win32;
@ -147,6 +147,7 @@ sub mkvcbuild
$libpq->AddIncludeDir('src\port');
$libpq->AddLibrary('wsock32.lib');
$libpq->AddLibrary('secur32.lib');
$libpq->AddLibrary('ws2_32.lib');
$libpq->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
$libpq->UseDef('src\interfaces\libpq\libpqdll.def');
$libpq->ReplaceFile('src\interfaces\libpq\libpqrc.c','src\interfaces\libpq\libpq.rc');