On mån, 2010-10-11 at 21:34 +0300, Peter Eisentraut wrote: > > * Why getnameinfo() in the fallback loop? > > I checked through my git history; this was actually a leftover from > some > debugging code. I'll remove it. > > > * Slash ('/') after the hostname > > > > At the parse_hba_line(), the parsed token which contains either > > hostname or cidr address is sliced into two parts on the first '/' > > character, if exist. > > Then, even if cidr_slash is not NULL, it shall be ignored when > > top-half of the token is hostname, not numeric address. > > OK, I'll fix that.
Hopefully final patch, which addresses the above issues, adds some documentation enhancements, and the possibility to quote host names (in case someone wants to have a host named "samehost").
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index 5cf2f29..0d515a1 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -80,9 +80,9 @@ A record is made up of a number of fields which are separated by spaces and/or tabs. Fields can contain white space if the field value is quoted. - Quoting one of the keywords in a database or user name field (e.g., + Quoting one of the keywords in a database, user, or address field (e.g., <literal>all</> or <literal>replication</>) makes the word lose its special - character, and just match a database or user with that name. + character, and just match a database, user, or host with that name. </para> <para> @@ -101,9 +101,9 @@ A record can have one of the seven formats <synopsis> local <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional> -host <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>CIDR-address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional> -hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>CIDR-address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional> -hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>CIDR-address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional> +host <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional> +hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional> +hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional> host <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional> hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional> hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional> @@ -218,13 +218,17 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> </varlistentry> <varlistentry> - <term><replaceable>CIDR-address</replaceable></term> + <term><replaceable>address</replaceable></term> <listitem> <para> Specifies the client machine IP address range that this record - matches. This field contains an IP address in standard dotted decimal - notation and a <acronym>CIDR</> mask length. (IP addresses can only be - specified numerically, not as domain or host names.) The mask + matches. This field can contain either a host name, an IP + address range, or one of the special key words mentioned below. + </para> + + <para> + An IP address is specified in standard dotted decimal + notation with a <acronym>CIDR</> mask length. The mask length indicates the number of high-order bits of the client IP address that must match. Bits to the right of this must be zero in the given IP address. @@ -233,14 +237,7 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> </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 + Typical examples of an IP address range specified this way are <literal>172.20.143.89/32</literal> for a single host, or <literal>172.20.143.0/24</literal> for a small network, or <literal>10.6.0.0/16</literal> for a larger one. @@ -260,6 +257,35 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> </para> <para> + You can also 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> + If a host name is specified (anything that is not an IP address + or a special key word is processed as a potential host name), + that name is compared with the result of a reverse name + resolution of the client's IP address (e.g., reverse DNS + lookup, if DNS is used). If there is a match, then a forward + name resolution (e.g., forward DNS lookup) is performed on the + host name to check if it resolves to an IP address that is + equal to the client's IP address. If both directions match, + then the entry is considered to match. + </para> + + <para> + When host names are specified + in <filename>pg_hba.conf</filename>, you should make sure that + name resolution is reasonably fast. It can be of advantage to + set up a local name resolution cache such + as <command>nscd</command>. Also, you may wish to enable the + configuration parameter <varname>log_hostname</varname> to see + the client's host name instead of the IP address in the log. + </para> + + <para> This field only applies to <literal>host</literal>, <literal>hostssl</literal>, and <literal>hostnossl</> records. </para> @@ -511,12 +537,12 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> # any database user name using Unix-domain sockets (the default for local # connections). # -# TYPE DATABASE USER CIDR-ADDRESS METHOD +# TYPE DATABASE USER ADDRESS METHOD local all all trust # The same using local loopback TCP/IP connections. # -# TYPE DATABASE USER CIDR-ADDRESS METHOD +# TYPE DATABASE USER ADDRESS METHOD host all all 127.0.0.1/32 trust # The same as the previous line, but using a separate netmask column @@ -524,17 +550,27 @@ host all all 127.0.0.1/32 trust # TYPE DATABASE USER IP-ADDRESS IP-MASK METHOD host all all 127.0.0.1 255.255.255.255 trust +# The same over IPv6. +# +# TYPE DATABASE USER ADDRESS METHOD +host all all ::1/128 trust + +# The same using a host name (would typically cover both IPv4 and IPv6). +# +# TYPE DATABASE USER ADDRESS METHOD +host all all localhost trust + # Allow any user from any host with IP address 192.168.93.x to connect # to database "postgres" as the same user name that ident reports for # the connection (typically the operating system user name). # -# TYPE DATABASE USER CIDR-ADDRESS METHOD +# TYPE DATABASE USER ADDRESS METHOD host postgres all 192.168.93.0/24 ident # Allow any user from host 192.168.12.10 to connect to database # "postgres" if the user's password is correctly supplied. # -# TYPE DATABASE USER CIDR-ADDRESS METHOD +# TYPE DATABASE USER ADDRESS METHOD host postgres all 192.168.12.10/32 md5 # In the absence of preceding "host" lines, these two lines will @@ -543,7 +579,7 @@ host postgres all 192.168.12.10/32 md5 # on the Internet. The zero mask causes no bits of the host IP # address to be considered, so it matches any host. # -# TYPE DATABASE USER CIDR-ADDRESS METHOD +# TYPE DATABASE USER ADDRESS METHOD host all all 192.168.54.1/32 reject host all all 0.0.0.0/0 krb5 @@ -553,7 +589,7 @@ host all all 0.0.0.0/0 krb5 # connection is allowed if there is an entry in pg_ident.conf for map # "omicron" that says "bryanh" is allowed to connect as "guest1". # -# TYPE DATABASE USER CIDR-ADDRESS METHOD +# TYPE DATABASE USER ADDRESS METHOD host all all 192.168.0.0/16 ident map=omicron # If these are the only three lines for local connections, they will @@ -563,7 +599,7 @@ host all all 192.168.0.0/16 ident map=omicro # $PGDATA/admins contains a list of names of administrators. Passwords # are required in all cases. # -# TYPE DATABASE USER CIDR-ADDRESS METHOD +# TYPE DATABASE USER ADDRESS METHOD local sameuser all md5 local all @admins md5 local all +support md5 diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 0227f9b..cd4fb3f 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,96 @@ 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 = strdup(remote_hostname); + } + + if (strcmp(port->remote_hostname, hostname) != 0) + return false; + + /* Lookup IP from host name and check against original IP */ + + ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result); + if (ret != 0) + ereport(ERROR, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("getaddrinfo failed on \"%s\": %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); + + return found; +} + /* * Check to see if a connecting IP matches the given address and netmask. */ @@ -782,12 +874,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 +908,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 +927,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 +960,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 +1477,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: diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample index e1017cf..87fed80 100644 --- a/src/backend/libpq/pg_hba.conf.sample +++ b/src/backend/libpq/pg_hba.conf.sample @@ -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-nolo...@# "local" is for Unix domain socket connections only @remove-line-for-nolo...@local all all @authmethod@ diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 6c16752..ff7713f 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -3420,6 +3420,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 diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index b8e8df0..eb6637f 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -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; diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index 83628c1..c7d4021 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -109,6 +109,7 @@ 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 */ char *remote_port; /* text rep of remote port */ CAC_state canAcceptConnections; /* postmaster connection status */
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers