The attached patch tries to implement regexp support in the usermaps
(pg_ident.conf).
The use-case will be to do things like realm-based matches in
kerberos/GSSAPI authentication (will require some further hacks on that
one to expose the realm name). For example you could have:
krb /^(.*)@myrealm.com$ \1
krb /^(.*)@otherrealm.com guest
and things like that.
It will also likely be quite useful once we get SSL certificate based
authentication.
Reviews particularly appreciated - I really can't claim to *know* our
regexp code :-O
Obviously docs need to be updated as well.
//Magnus
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 27,32 ****
--- 27,33 ----
#include "libpq/ip.h"
#include "libpq/libpq.h"
+ #include "regex/regex.h"
#include "storage/fd.h"
#include "utils/flatfiles.h"
#include "utils/guc.h"
***************
*** 1325,1344 **** parse_ident_usermap(List *line, int line_number, const char *usermap_name,
token = lfirst(line_item);
file_pgrole = token;
/* Match? */
! if (case_insensitive)
{
! 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;
}
else
{
! if (strcmp(file_map, usermap_name) == 0 &&
! strcmp(file_pgrole, pg_role) == 0 &&
! strcmp(file_ident_user, ident_user) == 0)
! *found_p = true;
}
return;
--- 1326,1453 ----
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 (file_ident_user[0] == '/')
{
! /*
! * 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 */
! regexp_pgrole = palloc0(strlen(file_pgrole)-2+(matches[1].rm_so-matches[1].rm_so));
! 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
{
! /* 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;
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers