diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index 228cfffda3372aadba2cc1c972a38411cb8eda5c..7ab9305604b609d1537baec62553d0461fc520a7 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -282,6 +282,14 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> to resolve an IP address.) </para> + <para> + A host name specification that starts with a dot + (<literal>.</literal>) matches a suffix of the actual host + name. So <literal>.example.com</literal> would match + <literal>foo.example.com</literal> (but not just + <literal>example.com</literal>). + </para> + <para> When host names are specified in <filename>pg_hba.conf</filename>, you should make sure that @@ -310,6 +318,12 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> everyone's problem. </para> + <para> + Also, a reverse lookup is necessary to implement the suffix + matching feature, because the actual client host name needs to + be known in order to match it against the pattern. + </para> + <para> Note that this behavior is consistent with other popular implementations of host name-based access control, such as the @@ -605,6 +619,12 @@ host postgres all 192.168.93.0/24 ident # TYPE DATABASE USER ADDRESS METHOD host postgres all 192.168.12.10/32 md5 +# Allow any user from hosts in the example.com domain to connect to +# any database if the user's password is correctly supplied. +# +# TYPE DATABASE USER ADDRESS METHOD +host all all .example.com md5 + # In the absence of preceding "host" lines, these two lines will # reject all connections from 192.168.54.1 (since that entry will be # matched first), but allow Kerberos 5 connections from anywhere else diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 173635996268c659ee9b14f0033a17316eb1e246..d9d11d81b9d0a455ecc4ee9b1fca96e8f07dfe28 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -564,6 +564,26 @@ ipv6eq(struct sockaddr_in6 *a, struct sockaddr_in6 *b) #endif /* HAVE_IPV6 */ +/* + * Check whether host name matches pattern. + */ +static bool +hostname_match(const char *pattern, const char *actual_hostname) +{ + if (pattern[0] == '.') /* suffix match */ + { + size_t plen = strlen(pattern); + size_t hlen = strlen(actual_hostname); + + if (hlen < plen) + return false; + + return (pg_strcasecmp(pattern, actual_hostname + (hlen - plen)) == 0); + } + else + return (pg_strcasecmp(pattern, actual_hostname) == 0); +} + /* * Check to see if a connecting IP matches a given host name. */ @@ -588,7 +608,7 @@ check_hostname(hbaPort *port, const char *hostname) port->remote_hostname = pstrdup(remote_hostname); } - if (pg_strcasecmp(port->remote_hostname, hostname) != 0) + if (!hostname_match(hostname, port->remote_hostname)) return false; /* Lookup IP from host name and check against original IP */ diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample index 87fed80eedf5eb9106b047f73a1fdd1105b3dd33..87f84991bca9459fc6a9342de6079941289499d4 100644 --- a/src/backend/libpq/pg_hba.conf.sample +++ b/src/backend/libpq/pg_hba.conf.sample @@ -32,7 +32,8 @@ # 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. +# specifies the number of significant bits in the mask. A host name +# that starts with a dot (.) matches a suffix of the actual host name. # 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,