On 29/12/2019 23:10, Vik Fearing wrote:
> On 29/12/2019 17:31, Tom Lane wrote:
>> Robert Haas <[email protected]> writes:
>>> On Sat, Dec 28, 2019 at 2:02 PM Vik Fearing <[email protected]>
>>> wrote:
>>>> I'm all for this (and even suggested it during the IRC conversation that
>>>> prompted this patch). It's rife with bikeshedding, though. My original
>>>> proposal was to use '&' and Andrew Gierth would have used ':'.
>>> I think this is a good proposal regardless of which character we
>>> decide to use. My order of preference from highest-to-lowest would
>>> probably be :*&, but maybe that's just because I'm reading this on
>>> Sunday rather than on Tuesday.
>> I don't have any particular objection to '&' if people prefer that.
>
> I wrote the patch so I got to decide. :-) I will also volunteer to do
> the grunt work of changing the symbol if consensus wants that, though.
>
>
> It turns out that my original patch didn't really change, all the meat
> is in the keywords patch. The superuser patch is to be applied on top
> of the keywords patch.
>
I missed a few places in the tap tests. New keywords patch attached,
superuser patch unchanged.
--
Vik Fearing
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 55694c4368..add25b2b43 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -8953,8 +8953,8 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
<entry><structfield>text</structfield></entry>
<entry>
Host name or IP address, or one
- of <literal>all</literal>, <literal>samehost</literal>,
- or <literal>samenet</literal>, or null for local connections
+ of <literal>&all</literal>, <literal>&samehost</literal>,
+ or <literal>&samenet</literal>, or null for local connections
</entry>
</row>
<row>
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 5f1eec78fb..96fc4b01e9 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -81,11 +81,26 @@
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 double-quoted.
- Quoting one of the keywords in a database, user, or address field (e.g.,
- <literal>all</literal> or <literal>replication</literal>) makes the word lose its special
- meaning, and just match a database, user, or host with that name.
</para>
+ <note>
+ <para>
+ As of version 13, keywords are preceded by the character "&".
+ For compatibility, the following legacy keywords are still accepted
+ on their own:
+ <literal>all</literal>, <literal>replication</literal>,
+ <literal>samegroup</literal>, <literal>samehost</literal>,
+ <literal>samenet</literal>, <literal>samerole</literal>,
+ <literal>sameuser</literal>.
+ </para>
+
+ <para>
+ Quoting one of these legacy keywords in a database, user, or address
+ field makes the word lose its special meaning, and just match a
+ database, user, or host with that name.
+ </para>
+ </note>
+
<para>
Each record specifies a connection type, a client IP address range
(if relevant for the connection type), a database name, a user name,
@@ -221,18 +236,18 @@ hostnogssenc <replaceable>database</replaceable> <replaceable>user</replaceable
<listitem>
<para>
Specifies which database name(s) this record matches. The value
- <literal>all</literal> specifies that it matches all databases.
- The value <literal>sameuser</literal> specifies that the record
+ <literal>&all</literal> specifies that it matches all databases.
+ The value <literal>&sameuser</literal> specifies that the record
matches if the requested database has the same name as the
- requested user. The value <literal>samerole</literal> specifies that
+ requested user. The value <literal>&samerole</literal> specifies that
the requested user must be a member of the role with the same
- name as the requested database. (<literal>samegroup</literal> is an
- obsolete but still accepted spelling of <literal>samerole</literal>.)
+ name as the requested database. (<literal>&samegroup</literal> is an
+ obsolete but still accepted spelling of <literal>&samerole</literal>.)
Superusers are not considered to be members of a role for the
- purposes of <literal>samerole</literal> unless they are explicitly
+ purposes of <literal>&samerole</literal> unless they are explicitly
members of the role, directly or indirectly, and not just by
virtue of being a superuser.
- The value <literal>replication</literal> specifies that the record
+ The value <literal>&replication</literal> specifies that the record
matches if a physical replication connection is requested (note that
replication connections do not specify any particular database).
Otherwise, this is the name of
@@ -249,7 +264,7 @@ hostnogssenc <replaceable>database</replaceable> <replaceable>user</replaceable
<listitem>
<para>
Specifies which database user name(s) this record
- matches. The value <literal>all</literal> specifies that it
+ matches. The value <literal>&all</literal> specifies that it
matches all users. Otherwise, this is either the name of a specific
database user, or a group name preceded by <literal>+</literal>.
(Recall that there is no real distinction between users and groups
@@ -312,9 +327,9 @@ hostnogssenc <replaceable>database</replaceable> <replaceable>user</replaceable
</para>
<para>
- You can also write <literal>all</literal> to match any IP address,
- <literal>samehost</literal> to match any of the server's own IP
- addresses, or <literal>samenet</literal> to match any address in any
+ You can also write <literal>&all</literal> to match any IP address,
+ <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>
@@ -699,40 +714,40 @@ hostnogssenc <replaceable>database</replaceable> <replaceable>user</replaceable
# connections).
#
# TYPE DATABASE USER ADDRESS METHOD
-local all all trust
+local &all &all trust
# The same using local loopback TCP/IP connections.
#
# TYPE DATABASE USER ADDRESS METHOD
-host all all 127.0.0.1/32 trust
+host &all &all 127.0.0.1/32 trust
# The same as the previous line, but using a separate netmask column
#
# TYPE DATABASE USER IP-ADDRESS IP-MASK METHOD
-host all all 127.0.0.1 255.255.255.255 trust
+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
+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
+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 ADDRESS METHOD
-host postgres all 192.168.93.0/24 ident
+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 ADDRESS METHOD
-host postgres all 192.168.12.10/32 scram-sha-256
+host postgres &all 192.168.12.10/32 scram-sha-256
# Allow any user from hosts in the example.com domain to connect to
# any database if the user's password is correctly supplied.
@@ -742,8 +757,8 @@ host postgres all 192.168.12.10/32 scram-sha-256
# authentication.
#
# TYPE DATABASE USER ADDRESS METHOD
-host all mike .example.com md5
-host all all .example.com scram-sha-256
+host &all mike .example.com md5
+host &all &all .example.com scram-sha-256
# In the absence of preceding "host" lines, these three lines will
# reject all connections from 192.168.54.1 (since that entry will be
@@ -754,9 +769,9 @@ host all all .example.com scram-sha-256
# encrypted GSSAPI connections) are allowed, but only from 192.168.12.10.
#
# TYPE DATABASE USER ADDRESS METHOD
-host all all 192.168.54.1/32 reject
-hostgssenc all all 0.0.0.0/0 gss
-host all all 192.168.12.10/32 gss
+host &all &all 192.168.54.1/32 reject
+hostgssenc &all &all 0.0.0.0/0 gss
+host &all &all 192.168.12.10/32 gss
# Allow users from 192.168.x.x hosts to connect to any database, if
# they pass the ident check. If, for example, ident says the user is
@@ -765,7 +780,7 @@ host all all 192.168.12.10/32 gss
# "omicron" that says "bryanh" is allowed to connect as "guest1".
#
# TYPE DATABASE USER ADDRESS METHOD
-host all all 192.168.0.0/16 ident map=omicron
+host &all &all 192.168.0.0/16 ident map=omicron
# If these are the only three lines for local connections, they will
# allow local users to connect only to their own databases (databases
@@ -775,15 +790,15 @@ host all all 192.168.0.0/16 ident map=omicro
# are required in all cases.
#
# TYPE DATABASE USER ADDRESS METHOD
-local sameuser all md5
-local all @admins md5
-local all +support md5
+local &sameuser &all md5
+local &all @admins md5
+local &all +support md5
# The last two lines above can be combined into a single line:
-local all @admins,+support md5
+local &all @admins,+support md5
# The database column can also use lists and file names:
-local db1,db2,@demodbs all md5
+local db1,db2,@demodbs &all md5
</programlisting>
</example>
</sect1>
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index b6de92783a..da18c389e5 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -64,8 +64,20 @@ typedef struct check_network_data
bool result; /* set to true if match */
} check_network_data;
+/*
+ * The following keywords are accepted without the keyword sigil for legacy
+ * reasons. They may become normal words at some point in the future.
+ */
+#define token_is_legacy_keyword(t, k) (!t->quoted && ( \
+ strcmp(t->string, "all") == 0 || \
+ strcmp(t->string, "replication") == 0 || \
+ strcmp(t->string, "samegroup") == 0 || \
+ strcmp(t->string, "samehost") == 0 || \
+ strcmp(t->string, "samenet") == 0 || \
+ strcmp(t->string, "samerole") == 0 || \
+ strcmp(t->string, "sameuser") == 0))
-#define token_is_keyword(t, k) (!t->quoted && strcmp(t->string, k) == 0)
+#define token_is_keyword(t, k) ((t->string[0] == '&' && strcmp(t->string+1, k) == 0) || token_is_legacy_keyword(t, k))
#define token_matches(t, k) (strcmp(t->string, k) == 0)
/*
@@ -2472,7 +2484,7 @@ fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
* Flatten HbaToken list to string list. It might seem that we
* should re-quote any quoted tokens, but that has been rejected
* on the grounds that it makes it harder to compare the array
- * elements to other system catalogs. That makes entries like
+ * elements to other system catalogs. That makes entries with legacy keywords like
* "all" or "samerole" formally ambiguous ... but users who name
* databases/roles that way are inflicting their own pain.
*/
diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample
index c853e36232..d08aba497f 100644
--- a/src/backend/libpq/pg_hba.conf.sample
+++ b/src/backend/libpq/pg_hba.conf.sample
@@ -21,12 +21,12 @@
# "hostssl" is an SSL-encrypted TCP/IP socket, and "hostnossl" is a
# plain TCP/IP socket.
#
-# DATABASE can be "all", "sameuser", "samerole", "replication", a
-# database name, or a comma-separated list thereof. The "all"
-# keyword does not match "replication". Access to replication
+# DATABASE can be &all, &sameuser, &samerole, &replication, a
+# database name, or a comma-separated list thereof. The &all
+# keyword does not match &replication. Access to replication
# must be enabled in a separate record (see example below).
#
-# USER can be "all", a user name, a group name prefixed with "+", or a
+# USER can be &all, a user name, a group name prefixed with "+", or a
# comma-separated list thereof. In both the DATABASE and USER fields
# you can also write a file name prefixed with "@" to include names
# from a separate file.
@@ -53,11 +53,18 @@
# section in the documentation for a list of which options are
# available for which authentication methods.
#
+# As of version 13, keywords are preceded by the character "&".
+# For compatibility, the following legacy keywords are still accepted on
+# their own:
+# all,
+# replication,
+# sameuser, samegroup, samerole,
+# samehost, samenet
+#
# Database and user names containing spaces, commas, quotes and other
-# special characters must be quoted. Quoting one of the keywords
-# "all", "sameuser", "samerole" or "replication" makes the name lose
-# its special character, and just match a database or username with
-# that name.
+# special characters must be quoted. Quoting one of the legacy keywords
+# from the previous paragraph makes the name lose its special character,
+# and just match a database or username with that name.
#
# This file is read on server startup and when the server receives a
# SIGHUP signal. If you edit the file on a running system, you have to
@@ -77,13 +84,13 @@
# TYPE DATABASE USER ADDRESS METHOD
@remove-line-for-nolocal@# "local" is for Unix domain socket connections only
-@remove-line-for-nolocal@local all all @authmethodlocal@
+@remove-line-for-nolocal@local &all &all @authmethodlocal@
# IPv4 local connections:
-host all all 127.0.0.1/32 @authmethodhost@
+host &all &all 127.0.0.1/32 @authmethodhost@
# IPv6 local connections:
-host all all ::1/128 @authmethodhost@
+host &all &all ::1/128 @authmethodhost@
# Allow replication connections from localhost, by a user with the
# replication privilege.
-@remove-line-for-nolocal@local replication all @authmethodlocal@
-host replication all 127.0.0.1/32 @authmethodhost@
-host replication all ::1/128 @authmethodhost@
+@remove-line-for-nolocal@local &replication &all @authmethodlocal@
+host &replication &all 127.0.0.1/32 @authmethodhost@
+host &replication &all ::1/128 @authmethodhost@
diff --git a/src/test/authentication/t/001_password.pl b/src/test/authentication/t/001_password.pl
index 5985130e3d..237a5ab53f 100644
--- a/src/test/authentication/t/001_password.pl
+++ b/src/test/authentication/t/001_password.pl
@@ -29,7 +29,7 @@ sub reset_pg_hba
my $hba_method = shift;
unlink($node->data_dir . '/pg_hba.conf');
- $node->append_conf('pg_hba.conf', "local all all $hba_method");
+ $node->append_conf('pg_hba.conf', "local &all &all $hba_method");
$node->reload;
return;
}
diff --git a/src/test/authentication/t/002_saslprep.pl b/src/test/authentication/t/002_saslprep.pl
index c4b335c45f..8d54f60710 100644
--- a/src/test/authentication/t/002_saslprep.pl
+++ b/src/test/authentication/t/002_saslprep.pl
@@ -25,7 +25,7 @@ sub reset_pg_hba
my $hba_method = shift;
unlink($node->data_dir . '/pg_hba.conf');
- $node->append_conf('pg_hba.conf', "local all all $hba_method");
+ $node->append_conf('pg_hba.conf', "local &all &all $hba_method");
$node->reload;
return;
}
diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl
index e3eb052160..108fd70243 100644
--- a/src/test/kerberos/t/001_auth.pl
+++ b/src/test/kerberos/t/001_auth.pl
@@ -197,7 +197,7 @@ sub test_access
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{host all all $hostaddr/32 gss map=mymap});
+ qq{host &all &all $hostaddr/32 gss map=mymap});
$node->restart;
test_access($node, 'test1', 'SELECT true', 2, '', 'fails without ticket');
@@ -233,7 +233,7 @@ test_access(
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{hostgssenc all all $hostaddr/32 gss map=mymap});
+ qq{hostgssenc &all &all $hostaddr/32 gss map=mymap});
$node->restart;
test_access(
@@ -255,7 +255,7 @@ test_access($node, "test1", 'SELECT true', 2, "gssencmode=disable",
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{hostnogssenc all all $hostaddr/32 gss map=mymap});
+ qq{hostnogssenc &all &all $hostaddr/32 gss map=mymap});
$node->restart;
test_access(
@@ -279,7 +279,7 @@ test_access(
truncate($node->data_dir . '/pg_ident.conf', 0);
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{host all all $hostaddr/32 gss include_realm=0});
+ qq{host &all &all $hostaddr/32 gss include_realm=0});
$node->restart;
test_access(
diff --git a/src/test/ldap/t/001_auth.pl b/src/test/ldap/t/001_auth.pl
index f8941144f5..f1eb2e8cfc 100644
--- a/src/test/ldap/t/001_auth.pl
+++ b/src/test/ldap/t/001_auth.pl
@@ -174,7 +174,7 @@ note "simple bind";
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapprefix="uid=" ldapsuffix=",dc=example,dc=net"}
+ qq{local &all &all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapprefix="uid=" ldapsuffix=",dc=example,dc=net"}
);
$node->restart;
@@ -190,7 +190,7 @@ note "search+bind";
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn"}
+ qq{local &all &all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn"}
);
$node->restart;
@@ -206,7 +206,7 @@ note "multiple servers";
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{local all all ldap ldapserver="$ldap_server $ldap_server" ldapport=$ldap_port ldapbasedn="$ldap_basedn"}
+ qq{local &all &all ldap ldapserver="$ldap_server $ldap_server" ldapport=$ldap_port ldapbasedn="$ldap_basedn"}
);
$node->restart;
@@ -222,7 +222,7 @@ note "LDAP URLs";
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{local all all ldap ldapurl="$ldap_url/$ldap_basedn?uid?sub"});
+ qq{local &all &all ldap ldapurl="$ldap_url/$ldap_basedn?uid?sub"});
$node->restart;
$ENV{"PGPASSWORD"} = 'wrong';
@@ -239,7 +239,7 @@ note "search filters";
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapsearchfilter="(|(uid=\$username)(mail=\$username))"}
+ qq{local &all &all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapsearchfilter="(|(uid=\$username)(mail=\$username))"}
);
$node->restart;
@@ -252,7 +252,7 @@ note "search filters in LDAP URLs";
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{local all all ldap ldapurl="$ldap_url/$ldap_basedn??sub?(|(uid=\$username)(mail=\$username))"}
+ qq{local &all &all ldap ldapurl="$ldap_url/$ldap_basedn??sub?(|(uid=\$username)(mail=\$username))"}
);
$node->restart;
@@ -266,7 +266,7 @@ test_access($node, '[email protected]', 0, 'search filter finds by mail');
# override. It might be useful in a case like this.
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{local all all ldap ldapurl="$ldap_url/$ldap_basedn??sub" ldapsearchfilter="(|(uid=\$username)(mail=\$username))"}
+ qq{local &all &all ldap ldapurl="$ldap_url/$ldap_basedn??sub" ldapsearchfilter="(|(uid=\$username)(mail=\$username))"}
);
$node->restart;
@@ -278,7 +278,7 @@ note "diagnostic message";
# note bad ldapprefix with a question mark that triggers a diagnostic message
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapprefix="?uid=" ldapsuffix=""}
+ qq{local &all &all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapprefix="?uid=" ldapsuffix=""}
);
$node->restart;
@@ -290,7 +290,7 @@ note "TLS";
# request StartTLS with ldaptls=1
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{local all all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapsearchfilter="(uid=\$username)" ldaptls=1}
+ qq{local &all &all ldap ldapserver=$ldap_server ldapport=$ldap_port ldapbasedn="$ldap_basedn" ldapsearchfilter="(uid=\$username)" ldaptls=1}
);
$node->restart;
@@ -300,7 +300,7 @@ test_access($node, 'test1', 0, 'StartTLS');
# request LDAPS with ldapscheme=ldaps
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{local all all ldap ldapserver=$ldap_server ldapscheme=ldaps ldapport=$ldaps_port ldapbasedn="$ldap_basedn" ldapsearchfilter="(uid=\$username)"}
+ qq{local &all &all ldap ldapserver=$ldap_server ldapscheme=ldaps ldapport=$ldaps_port ldapbasedn="$ldap_basedn" ldapsearchfilter="(uid=\$username)"}
);
$node->restart;
@@ -310,7 +310,7 @@ test_access($node, 'test1', 0, 'LDAPS');
# request LDAPS with ldapurl=ldaps://...
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{local all all ldap ldapurl="$ldaps_url/$ldap_basedn??sub?(uid=\$username)"}
+ qq{local &all &all ldap ldapurl="$ldaps_url/$ldap_basedn??sub?(uid=\$username)"}
);
$node->restart;
@@ -320,7 +320,7 @@ test_access($node, 'test1', 0, 'LDAPS with URL');
# bad combination of LDAPS and StartTLS
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{local all all ldap ldapurl="$ldaps_url/$ldap_basedn??sub?(uid=\$username)" ldaptls=1}
+ qq{local &all &all ldap ldapurl="$ldaps_url/$ldap_basedn??sub?(uid=\$username)" ldaptls=1}
);
$node->restart;
diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm
index 270bd6c856..b39d8cf25a 100644
--- a/src/test/perl/PostgresNode.pm
+++ b/src/test/perl/PostgresNode.pm
@@ -390,7 +390,7 @@ sub set_replication_conf
if ($TestLib::windows_os)
{
print $hba
- "host replication all $test_localhost/32 sspi include_realm=1 map=regress\n";
+ "host &replication &all $test_localhost/32 sspi include_realm=1 map=regress\n";
}
close $hba;
return;
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 297b8fbd6f..650fe38767 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -1065,10 +1065,10 @@ config_sspi_auth(const char *pgdata, const char *superuser_name)
exit(2);
}
CW(fputs("# Configuration written by config_sspi_auth()\n", hba) >= 0);
- CW(fputs("host all all 127.0.0.1/32 sspi include_realm=1 map=regress\n",
+ CW(fputs("host &all &all 127.0.0.1/32 sspi include_realm=1 map=regress\n",
hba) >= 0);
if (have_ipv6)
- CW(fputs("host all all ::1/128 sspi include_realm=1 map=regress\n",
+ CW(fputs("host &all &all ::1/128 sspi include_realm=1 map=regress\n",
hba) >= 0);
CW(fclose(hba) == 0);
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 96fc4b01e9..36cdf180c4 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -265,7 +265,11 @@ hostnogssenc <replaceable>database</replaceable> <replaceable>user</replaceable
<para>
Specifies which database user name(s) this record
matches. The value <literal>&all</literal> specifies that it
- matches all users. Otherwise, this is either the name of a specific
+ matches all users.
+ The value <literal>&superuser</literal> specifies that it matches all
+ superusers. The value <literal>&nonsuperuser</literal> specifies that it
+ matches no superusers.
+ Otherwise, this is either the name of a specific
database user, or a group name preceded by <literal>+</literal>.
(Recall that there is no real distinction between users and groups
in <productname>PostgreSQL</productname>; a <literal>+</literal> mark really means
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index da18c389e5..087680be64 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -608,6 +608,16 @@ check_role(const char *role, Oid roleid, List *tokens)
if (is_member(roleid, tok->string + 1))
return true;
}
+ else if (token_is_keyword(tok, "superuser"))
+ {
+ if (superuser_arg(roleid))
+ return true;
+ }
+ else if (token_is_keyword(tok, "nonsuperuser"))
+ {
+ if (!superuser_arg(roleid))
+ return true;
+ }
else if (token_matches(tok, role) ||
token_is_keyword(tok, "all"))
return true;