aboutsummaryrefslogtreecommitdiff
path: root/src/backend/libpq/hba.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/libpq/hba.c')
-rw-r--r--src/backend/libpq/hba.c147
1 files changed, 135 insertions, 12 deletions
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 0227f9b67c7..3f50349baf4 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -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: