mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Support host names in pg_hba.conf
Peter Eisentraut, reviewed by KaiGai Kohei and Tom Lane
This commit is contained in:
@ -102,8 +102,8 @@ pg_isblank(const char c)
|
||||
* whichever comes first. If no more tokens on line, position the file to the
|
||||
* beginning of the next line or EOF, whichever comes first.
|
||||
*
|
||||
* Handle comments. Treat unquoted keywords that might be role names or
|
||||
* database names specially, by appending a newline to them. Also, when
|
||||
* Handle comments. Treat unquoted keywords that might be role, database, or
|
||||
* host names specially, by appending a newline to them. Also, when
|
||||
* a token is terminated by a comma, the comma is included in the returned
|
||||
* token.
|
||||
*/
|
||||
@ -198,6 +198,8 @@ next_token(FILE *fp, char *buf, int bufsz, bool *initial_quote)
|
||||
|
||||
if (!saw_quote &&
|
||||
(strcmp(start_buf, "all") == 0 ||
|
||||
strcmp(start_buf, "samehost") == 0 ||
|
||||
strcmp(start_buf, "samenet") == 0 ||
|
||||
strcmp(start_buf, "sameuser") == 0 ||
|
||||
strcmp(start_buf, "samegroup") == 0 ||
|
||||
strcmp(start_buf, "samerole") == 0 ||
|
||||
@ -540,6 +542,102 @@ check_db(const char *dbname, const char *role, Oid roleid, char *param_str)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
ipv4eq(struct sockaddr_in *a, struct sockaddr_in *b)
|
||||
{
|
||||
return (a->sin_addr.s_addr == b->sin_addr.s_addr);
|
||||
}
|
||||
|
||||
static bool
|
||||
ipv6eq(struct sockaddr_in6 *a, struct sockaddr_in6 *b)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
if (a->sin6_addr.s6_addr[i] != b->sin6_addr.s6_addr[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see if a connecting IP matches a given host name.
|
||||
*/
|
||||
static bool
|
||||
check_hostname(hbaPort *port, const char *hostname)
|
||||
{
|
||||
struct addrinfo *gai_result, *gai;
|
||||
int ret;
|
||||
bool found;
|
||||
|
||||
/* Lookup remote host name if not already done */
|
||||
if (!port->remote_hostname)
|
||||
{
|
||||
char remote_hostname[NI_MAXHOST];
|
||||
|
||||
if (pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
|
||||
remote_hostname, sizeof(remote_hostname),
|
||||
NULL, 0,
|
||||
0))
|
||||
return false;
|
||||
|
||||
port->remote_hostname = pstrdup(remote_hostname);
|
||||
}
|
||||
|
||||
if (pg_strcasecmp(port->remote_hostname, hostname) != 0)
|
||||
return false;
|
||||
|
||||
/* Lookup IP from host name and check against original IP */
|
||||
|
||||
if (port->remote_hostname_resolv == +1)
|
||||
return true;
|
||||
if (port->remote_hostname_resolv == -1)
|
||||
return false;
|
||||
|
||||
ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result);
|
||||
if (ret != 0)
|
||||
ereport(ERROR,
|
||||
(errmsg("could not translate host name \"%s\" to address: %s",
|
||||
port->remote_hostname, gai_strerror(ret))));
|
||||
|
||||
found = false;
|
||||
for (gai = gai_result; gai; gai = gai->ai_next)
|
||||
{
|
||||
if (gai->ai_addr->sa_family == port->raddr.addr.ss_family)
|
||||
{
|
||||
if (gai->ai_addr->sa_family == AF_INET)
|
||||
{
|
||||
if (ipv4eq((struct sockaddr_in *) gai->ai_addr,
|
||||
(struct sockaddr_in *) &port->raddr.addr))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (gai->ai_addr->sa_family == AF_INET6)
|
||||
{
|
||||
if (ipv6eq((struct sockaddr_in6 *) gai->ai_addr,
|
||||
(struct sockaddr_in6 *) &port->raddr.addr))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gai_result)
|
||||
freeaddrinfo(gai_result);
|
||||
|
||||
if (!found)
|
||||
elog(DEBUG2, "pg_hba.conf host name \"%s\" rejected because address resolution did not return a match with IP address of client",
|
||||
hostname);
|
||||
|
||||
port->remote_hostname_resolv = found ? +1 : -1;
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check to see if a connecting IP matches the given address and netmask.
|
||||
*/
|
||||
@ -782,12 +880,12 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
|
||||
token = lfirst(line_item);
|
||||
|
||||
/* Is it equal to 'samehost' or 'samenet'? */
|
||||
if (strcmp(token, "samehost") == 0)
|
||||
if (strcmp(token, "samehost\n") == 0)
|
||||
{
|
||||
/* Any IP on this host is allowed to connect */
|
||||
parsedline->ip_cmp_method = ipCmpSameHost;
|
||||
}
|
||||
else if (strcmp(token, "samenet") == 0)
|
||||
else if (strcmp(token, "samenet\n") == 0)
|
||||
{
|
||||
/* Any IP on the host's subnets is allowed to connect */
|
||||
parsedline->ip_cmp_method = ipCmpSameNet;
|
||||
@ -816,7 +914,12 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
|
||||
hints.ai_next = NULL;
|
||||
|
||||
ret = pg_getaddrinfo_all(token, NULL, &hints, &gai_result);
|
||||
if (ret || !gai_result)
|
||||
if (ret == 0 && gai_result)
|
||||
memcpy(&parsedline->addr, gai_result->ai_addr,
|
||||
gai_result->ai_addrlen);
|
||||
else if (ret == EAI_NONAME)
|
||||
parsedline->hostname = token;
|
||||
else
|
||||
{
|
||||
ereport(LOG,
|
||||
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
||||
@ -830,13 +933,24 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
|
||||
return false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (parsedline->hostname)
|
||||
{
|
||||
*cidr_slash = '/'; /* restore token for message */
|
||||
ereport(LOG,
|
||||
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
||||
errmsg("specifying both host name and CIDR mask is invalid: \"%s\"",
|
||||
token),
|
||||
errcontext("line %d of configuration file \"%s\"",
|
||||
line_num, HbaFileName)));
|
||||
pfree(token);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
|
||||
parsedline->addr.ss_family) < 0)
|
||||
{
|
||||
@ -852,7 +966,7 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
|
||||
}
|
||||
pfree(token);
|
||||
}
|
||||
else
|
||||
else if (!parsedline->hostname)
|
||||
{
|
||||
/* Read the mask field. */
|
||||
pfree(token);
|
||||
@ -1369,10 +1483,19 @@ check_hba(hbaPort *port)
|
||||
switch (hba->ip_cmp_method)
|
||||
{
|
||||
case ipCmpMask:
|
||||
if (!check_ip(&port->raddr,
|
||||
(struct sockaddr *) & hba->addr,
|
||||
(struct sockaddr *) & hba->mask))
|
||||
continue;
|
||||
if (hba->hostname)
|
||||
{
|
||||
if (!check_hostname(port,
|
||||
hba->hostname))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!check_ip(&port->raddr,
|
||||
(struct sockaddr *) & hba->addr,
|
||||
(struct sockaddr *) & hba->mask))
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case ipCmpSameHost:
|
||||
case ipCmpSameNet:
|
||||
|
@ -10,9 +10,9 @@
|
||||
# databases they can access. Records take one of these forms:
|
||||
#
|
||||
# local DATABASE USER METHOD [OPTIONS]
|
||||
# host DATABASE USER CIDR-ADDRESS METHOD [OPTIONS]
|
||||
# hostssl DATABASE USER CIDR-ADDRESS METHOD [OPTIONS]
|
||||
# hostnossl DATABASE USER CIDR-ADDRESS METHOD [OPTIONS]
|
||||
# host DATABASE USER ADDRESS METHOD [OPTIONS]
|
||||
# hostssl DATABASE USER ADDRESS METHOD [OPTIONS]
|
||||
# hostnossl DATABASE USER ADDRESS METHOD [OPTIONS]
|
||||
#
|
||||
# (The uppercase items must be replaced by actual values.)
|
||||
#
|
||||
@ -29,14 +29,15 @@
|
||||
# you can also write a file name prefixed with "@" to include names
|
||||
# from a separate file.
|
||||
#
|
||||
# CIDR-ADDRESS specifies the set of hosts the record matches. It is
|
||||
# made up of an IP address and a CIDR mask that is an integer (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.
|
||||
# ADDRESS specifies the set of hosts the record matches. It can be a
|
||||
# host name, or it is made up of an IP address and a CIDR mask that is
|
||||
# an integer (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", "radius" or "cert". Note that
|
||||
@ -70,7 +71,7 @@
|
||||
|
||||
@authcomment@
|
||||
|
||||
# TYPE DATABASE USER CIDR-ADDRESS METHOD
|
||||
# TYPE DATABASE USER ADDRESS METHOD
|
||||
|
||||
@remove-line-for-nolocal@# "local" is for Unix domain socket connections only
|
||||
@remove-line-for-nolocal@local all all @authmethod@
|
||||
|
@ -3422,6 +3422,8 @@ BackendInitialize(Port *port)
|
||||
*/
|
||||
port->remote_host = strdup(remote_host);
|
||||
port->remote_port = strdup(remote_port);
|
||||
if (log_hostname)
|
||||
port->remote_hostname = port->remote_host;
|
||||
|
||||
/*
|
||||
* Ready to begin client interaction. We will give up and exit(1) after a
|
||||
|
@ -56,6 +56,7 @@ typedef struct
|
||||
struct sockaddr_storage addr;
|
||||
struct sockaddr_storage mask;
|
||||
IPCompareMethod ip_cmp_method;
|
||||
char *hostname;
|
||||
UserAuth auth_method;
|
||||
|
||||
char *usermap;
|
||||
|
@ -109,6 +109,10 @@ typedef struct Port
|
||||
SockAddr laddr; /* local addr (postmaster) */
|
||||
SockAddr raddr; /* remote addr (client) */
|
||||
char *remote_host; /* name (or ip addr) of remote host */
|
||||
char *remote_hostname; /* name (not ip addr) of remote host, if available */
|
||||
int remote_hostname_resolv; /* +1 = remote_hostname is known to resolve to client's IP address;
|
||||
-1 = remote_hostname is known NOT to resolve to client's IP address;
|
||||
0 = we have not done the forward DNS lookup yet */
|
||||
char *remote_port; /* text rep of remote port */
|
||||
CAC_state canAcceptConnections; /* postmaster connection status */
|
||||
|
||||
|
Reference in New Issue
Block a user