mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-03-19 20:00:51 +08:00
Support regular expressions in pg_ident.conf.
This commit is contained in:
parent
2939e20037
commit
5d2a1a41d0
@ -1,4 +1,4 @@
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.113 2008/11/20 20:45:29 momjian Exp $ -->
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.114 2008/11/28 14:26:58 mha Exp $ -->
|
||||
|
||||
<chapter id="client-authentication">
|
||||
<title>Client Authentication</title>
|
||||
@ -595,6 +595,19 @@ local db1,db2,@demodbs all md5
|
||||
There is no restriction regarding how many database users a given
|
||||
operating system user can correspond to, nor vice versa.
|
||||
</para>
|
||||
<para>
|
||||
If the <replaceable>system-username</> field starts with a slash (<literal>/</>),
|
||||
the contents of the field is treated as a regular expression. This regular
|
||||
expression supports a single capture, which can be back-referenced as
|
||||
<literal>\1</> (backslash-one). This allows the mapping of different syntax
|
||||
names with a single line.
|
||||
<programlisting>
|
||||
mymap /(.*)@mydomain.com \1
|
||||
mymap /(.*)@otherdomain.com guest
|
||||
</programlisting>
|
||||
will "remove" the domain part for users with system usernames @mydomain.com, and
|
||||
allow all users from @otherdomain.com to log in as guest.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <filename>pg_ident.conf</filename> file is read on start-up and
|
||||
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.175 2008/11/20 20:45:30 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.176 2008/11/28 14:26:58 mha Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -27,6 +27,7 @@
|
||||
|
||||
#include "libpq/ip.h"
|
||||
#include "libpq/libpq.h"
|
||||
#include "regex/regex.h"
|
||||
#include "storage/fd.h"
|
||||
#include "utils/flatfiles.h"
|
||||
#include "utils/guc.h"
|
||||
@ -1403,20 +1404,128 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name,
|
||||
token = lfirst(line_item);
|
||||
file_pgrole = token;
|
||||
|
||||
if (strcmp(file_map, usermap_name) != 0)
|
||||
/* Line does not match the map name we're looking for, so just abort */
|
||||
return;
|
||||
|
||||
/* Match? */
|
||||
if (case_insensitive)
|
||||
if (file_ident_user[0] == '/')
|
||||
{
|
||||
if (strcmp(file_map, usermap_name) == 0 &&
|
||||
pg_strcasecmp(file_pgrole, pg_role) == 0 &&
|
||||
pg_strcasecmp(file_ident_user, ident_user) == 0)
|
||||
*found_p = true;
|
||||
/*
|
||||
* When system username starts with a slash, treat it as a regular expression.
|
||||
* In this case, we process the system username as a regular expression that
|
||||
* returns exactly one match. This is replaced for \1 in the database username
|
||||
* string, if present.
|
||||
*/
|
||||
int r;
|
||||
regex_t re;
|
||||
regmatch_t matches[2];
|
||||
pg_wchar *wstr;
|
||||
int wlen;
|
||||
char *ofs;
|
||||
char *regexp_pgrole;
|
||||
|
||||
wstr = palloc((strlen(file_ident_user+1) + 1) * sizeof(pg_wchar));
|
||||
wlen = pg_mb2wchar_with_len(file_ident_user+1, wstr, strlen(file_ident_user+1));
|
||||
|
||||
/*
|
||||
* XXX: Major room for optimization: regexps could be compiled when the file is loaded
|
||||
* and then re-used in every connection.
|
||||
*/
|
||||
r = pg_regcomp(&re, wstr, wlen, REG_ADVANCED);
|
||||
if (r)
|
||||
{
|
||||
char errstr[100];
|
||||
|
||||
pg_regerror(r, &re, errstr, sizeof(errstr));
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
|
||||
errmsg("invalid regular expression '%s': %s", file_ident_user+1, errstr)));
|
||||
|
||||
pfree(wstr);
|
||||
*error_p = true;
|
||||
return;
|
||||
}
|
||||
pfree(wstr);
|
||||
|
||||
wstr = palloc((strlen(ident_user) + 1) * sizeof(pg_wchar));
|
||||
wlen = pg_mb2wchar_with_len(ident_user, wstr, strlen(ident_user));
|
||||
|
||||
r = pg_regexec(&re, wstr, wlen, 0, NULL, 2, matches,0);
|
||||
if (r)
|
||||
{
|
||||
char errstr[100];
|
||||
|
||||
if (r != REG_NOMATCH)
|
||||
{
|
||||
/* REG_NOMATCH is not an error, everything else is */
|
||||
pg_regerror(r, &re, errstr, sizeof(errstr));
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
|
||||
errmsg("regular expression match for '%s' failed: %s", file_ident_user+1, errstr)));
|
||||
*error_p = true;
|
||||
}
|
||||
|
||||
pfree(wstr);
|
||||
pg_regfree(&re);
|
||||
return;
|
||||
}
|
||||
pfree(wstr);
|
||||
|
||||
if ((ofs = strstr(file_pgrole, "\\1")) != NULL)
|
||||
{
|
||||
/* substitution of the first argument requested */
|
||||
if (matches[1].rm_so < 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
|
||||
errmsg("regular expression '%s' has no subexpressions as requested by backreference in '%s'",
|
||||
file_ident_user+1, file_pgrole)));
|
||||
/* length: original length minus length of \1 plus length of match plus null terminator */
|
||||
regexp_pgrole = palloc0(strlen(file_pgrole) - 2 + (matches[1].rm_eo-matches[1].rm_so) + 1);
|
||||
strncpy(regexp_pgrole, file_pgrole, (ofs-file_pgrole));
|
||||
memcpy(regexp_pgrole+strlen(regexp_pgrole),
|
||||
ident_user+matches[1].rm_so,
|
||||
matches[1].rm_eo-matches[1].rm_so);
|
||||
strcat(regexp_pgrole, ofs+2);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no substitution, so copy the match */
|
||||
regexp_pgrole = pstrdup(file_pgrole);
|
||||
}
|
||||
|
||||
pg_regfree(&re);
|
||||
|
||||
/* now check if the username actually matched what the user is trying to connect as */
|
||||
if (case_insensitive)
|
||||
{
|
||||
if (pg_strcasecmp(regexp_pgrole, pg_role) == 0)
|
||||
*found_p = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strcmp(regexp_pgrole, pg_role) == 0)
|
||||
*found_p = true;
|
||||
}
|
||||
pfree(regexp_pgrole);
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strcmp(file_map, usermap_name) == 0 &&
|
||||
strcmp(file_pgrole, pg_role) == 0 &&
|
||||
strcmp(file_ident_user, ident_user) == 0)
|
||||
*found_p = true;
|
||||
/* Not regular expression, so make complete match */
|
||||
if (case_insensitive)
|
||||
{
|
||||
if (pg_strcasecmp(file_pgrole, pg_role) == 0 &&
|
||||
pg_strcasecmp(file_ident_user, ident_user) == 0)
|
||||
*found_p = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strcmp(file_pgrole, pg_role) == 0 &&
|
||||
strcmp(file_ident_user, ident_user) == 0)
|
||||
*found_p = true;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -17,8 +17,13 @@
|
||||
# pg_hba.conf. SYSTEM-USERNAME is the detected user name of the
|
||||
# client. PG-USERNAME is the requested PostgreSQL user name. The
|
||||
# existence of a record specifies that SYSTEM-USERNAME may connect as
|
||||
# PG-USERNAME. Multiple maps may be specified in this file and used
|
||||
# by pg_hba.conf.
|
||||
# PG-USERNAME.
|
||||
#
|
||||
# If SYSTEM-USERNAME starts with a slash (/), it will be treated as
|
||||
# a regular expression. Up to one capture is allowed, and this will
|
||||
# be replaced in PG-USERNAME for backslash-one (\1) if present.
|
||||
#
|
||||
# Multiple maps may be specified in this file and used # by pg_hba.conf.
|
||||
#
|
||||
# This file is read on server startup and when the postmaster receives
|
||||
# a SIGHUP signal. If you edit the file on a running system, you have
|
||||
|
Loading…
x
Reference in New Issue
Block a user