Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 5d2a1a4

Browse files
committed
Support regular expressions in pg_ident.conf.
1 parent 2939e20 commit 5d2a1a4

File tree

3 files changed

+140
-13
lines changed

3 files changed

+140
-13
lines changed

doc/src/sgml/client-auth.sgml

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.113 2008/11/20 20:45:29 momjian Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.114 2008/11/28 14:26:58 mha Exp $ -->
22

33
<chapter id="client-authentication">
44
<title>Client Authentication</title>
@@ -595,6 +595,19 @@ local db1,db2,@demodbs all md5
595595
There is no restriction regarding how many database users a given
596596
operating system user can correspond to, nor vice versa.
597597
</para>
598+
<para>
599+
If the <replaceable>system-username</> field starts with a slash (<literal>/</>),
600+
the contents of the field is treated as a regular expression. This regular
601+
expression supports a single capture, which can be back-referenced as
602+
<literal>\1</> (backslash-one). This allows the mapping of different syntax
603+
names with a single line.
604+
<programlisting>
605+
mymap /(.*)@mydomain.com \1
606+
mymap /(.*)@otherdomain.com guest
607+
</programlisting>
608+
will "remove" the domain part for users with system usernames @mydomain.com, and
609+
allow all users from @otherdomain.com to log in as guest.
610+
</para>
598611

599612
<para>
600613
The <filename>pg_ident.conf</filename> file is read on start-up and

src/backend/libpq/hba.c

+119-10
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*
1111
*
1212
* IDENTIFICATION
13-
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.175 2008/11/20 20:45:30 momjian Exp $
13+
* $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.176 2008/11/28 14:26:58 mha Exp $
1414
*
1515
*-------------------------------------------------------------------------
1616
*/
@@ -27,6 +27,7 @@
2727

2828
#include "libpq/ip.h"
2929
#include "libpq/libpq.h"
30+
#include "regex/regex.h"
3031
#include "storage/fd.h"
3132
#include "utils/flatfiles.h"
3233
#include "utils/guc.h"
@@ -1403,20 +1404,128 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name,
14031404
token = lfirst(line_item);
14041405
file_pgrole = token;
14051406

1407+
if (strcmp(file_map, usermap_name) != 0)
1408+
/* Line does not match the map name we're looking for, so just abort */
1409+
return;
1410+
14061411
/* Match? */
1407-
if (case_insensitive)
1412+
if (file_ident_user[0] == '/')
14081413
{
1409-
if (strcmp(file_map, usermap_name) == 0 &&
1410-
pg_strcasecmp(file_pgrole, pg_role) == 0 &&
1411-
pg_strcasecmp(file_ident_user, ident_user) == 0)
1412-
*found_p = true;
1414+
/*
1415+
* When system username starts with a slash, treat it as a regular expression.
1416+
* In this case, we process the system username as a regular expression that
1417+
* returns exactly one match. This is replaced for \1 in the database username
1418+
* string, if present.
1419+
*/
1420+
int r;
1421+
regex_t re;
1422+
regmatch_t matches[2];
1423+
pg_wchar *wstr;
1424+
int wlen;
1425+
char *ofs;
1426+
char *regexp_pgrole;
1427+
1428+
wstr = palloc((strlen(file_ident_user+1) + 1) * sizeof(pg_wchar));
1429+
wlen = pg_mb2wchar_with_len(file_ident_user+1, wstr, strlen(file_ident_user+1));
1430+
1431+
/*
1432+
* XXX: Major room for optimization: regexps could be compiled when the file is loaded
1433+
* and then re-used in every connection.
1434+
*/
1435+
r = pg_regcomp(&re, wstr, wlen, REG_ADVANCED);
1436+
if (r)
1437+
{
1438+
char errstr[100];
1439+
1440+
pg_regerror(r, &re, errstr, sizeof(errstr));
1441+
ereport(ERROR,
1442+
(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
1443+
errmsg("invalid regular expression '%s': %s", file_ident_user+1, errstr)));
1444+
1445+
pfree(wstr);
1446+
*error_p = true;
1447+
return;
1448+
}
1449+
pfree(wstr);
1450+
1451+
wstr = palloc((strlen(ident_user) + 1) * sizeof(pg_wchar));
1452+
wlen = pg_mb2wchar_with_len(ident_user, wstr, strlen(ident_user));
1453+
1454+
r = pg_regexec(&re, wstr, wlen, 0, NULL, 2, matches,0);
1455+
if (r)
1456+
{
1457+
char errstr[100];
1458+
1459+
if (r != REG_NOMATCH)
1460+
{
1461+
/* REG_NOMATCH is not an error, everything else is */
1462+
pg_regerror(r, &re, errstr, sizeof(errstr));
1463+
ereport(ERROR,
1464+
(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
1465+
errmsg("regular expression match for '%s' failed: %s", file_ident_user+1, errstr)));
1466+
*error_p = true;
1467+
}
1468+
1469+
pfree(wstr);
1470+
pg_regfree(&re);
1471+
return;
1472+
}
1473+
pfree(wstr);
1474+
1475+
if ((ofs = strstr(file_pgrole, "\\1")) != NULL)
1476+
{
1477+
/* substitution of the first argument requested */
1478+
if (matches[1].rm_so < 0)
1479+
ereport(ERROR,
1480+
(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
1481+
errmsg("regular expression '%s' has no subexpressions as requested by backreference in '%s'",
1482+
file_ident_user+1, file_pgrole)));
1483+
/* length: original length minus length of \1 plus length of match plus null terminator */
1484+
regexp_pgrole = palloc0(strlen(file_pgrole) - 2 + (matches[1].rm_eo-matches[1].rm_so) + 1);
1485+
strncpy(regexp_pgrole, file_pgrole, (ofs-file_pgrole));
1486+
memcpy(regexp_pgrole+strlen(regexp_pgrole),
1487+
ident_user+matches[1].rm_so,
1488+
matches[1].rm_eo-matches[1].rm_so);
1489+
strcat(regexp_pgrole, ofs+2);
1490+
}
1491+
else
1492+
{
1493+
/* no substitution, so copy the match */
1494+
regexp_pgrole = pstrdup(file_pgrole);
1495+
}
1496+
1497+
pg_regfree(&re);
1498+
1499+
/* now check if the username actually matched what the user is trying to connect as */
1500+
if (case_insensitive)
1501+
{
1502+
if (pg_strcasecmp(regexp_pgrole, pg_role) == 0)
1503+
*found_p = true;
1504+
}
1505+
else
1506+
{
1507+
if (strcmp(regexp_pgrole, pg_role) == 0)
1508+
*found_p = true;
1509+
}
1510+
pfree(regexp_pgrole);
1511+
1512+
return;
14131513
}
14141514
else
14151515
{
1416-
if (strcmp(file_map, usermap_name) == 0 &&
1417-
strcmp(file_pgrole, pg_role) == 0 &&
1418-
strcmp(file_ident_user, ident_user) == 0)
1419-
*found_p = true;
1516+
/* Not regular expression, so make complete match */
1517+
if (case_insensitive)
1518+
{
1519+
if (pg_strcasecmp(file_pgrole, pg_role) == 0 &&
1520+
pg_strcasecmp(file_ident_user, ident_user) == 0)
1521+
*found_p = true;
1522+
}
1523+
else
1524+
{
1525+
if (strcmp(file_pgrole, pg_role) == 0 &&
1526+
strcmp(file_ident_user, ident_user) == 0)
1527+
*found_p = true;
1528+
}
14201529
}
14211530

14221531
return;

src/backend/libpq/pg_ident.conf.sample

+7-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,13 @@
1717
# pg_hba.conf. SYSTEM-USERNAME is the detected user name of the
1818
# client. PG-USERNAME is the requested PostgreSQL user name. The
1919
# existence of a record specifies that SYSTEM-USERNAME may connect as
20-
# PG-USERNAME. Multiple maps may be specified in this file and used
21-
# by pg_hba.conf.
20+
# PG-USERNAME.
21+
#
22+
# If SYSTEM-USERNAME starts with a slash (/), it will be treated as
23+
# a regular expression. Up to one capture is allowed, and this will
24+
# be replaced in PG-USERNAME for backslash-one (\1) if present.
25+
#
26+
# Multiple maps may be specified in this file and used # by pg_hba.conf.
2227
#
2328
# This file is read on server startup and when the postmaster receives
2429
# a SIGHUP signal. If you edit the file on a running system, you have

0 commit comments

Comments
 (0)