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 (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to