Re: usermap regexp support - Mailing list pgsql-hackers

From Magnus Hagander
Subject Re: usermap regexp support
Date
Msg-id 49198FA5.6020206@hagander.net
Whole thread Raw
In response to Re: usermap regexp support  (Gianni Ciolli <gianni.ciolli@2ndquadrant.it>)
Responses [REVIEW] (was Re: usermap regexp support)
List pgsql-hackers
Gianni Ciolli wrote:
> On Tue, Oct 28, 2008 at 08:59:53PM +0100, Magnus Hagander wrote:
>> The attached patch tries to implement regexp support in the usermaps
>> (pg_ident.conf).
>
> Hi Magnus,
>
> I am currently reviewing your patch.

Hi!

Thanks for the review!


> I found out that the execution of
>
>       pfree(regexp_pgrole);
>
> (there's only one such line in your patch) might raise on the current
> HEAD the following message
>
>       WARNING:  detected write past chunk end in Postmaster 0x9b13650

Yes, that's a stupid bug:
-                       /* 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));
+                       /* 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);

Two bugs: the length calculation used the start of the string vs the
start of the string, instead of end vs start. And there was no room for
the NULL terminator.

Attached is an updated version of the patch that fixes this.

//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 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
      {
!         /* 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;

pgsql-hackers by date:

Previous
From: Alvaro Herrera
Date:
Subject: Re: Duplicated docs on libpq parameters
Next
From: "Hiroshi Saito"
Date:
Subject: Re: [PATCHES] Solve a problem of LC_TIME of windows.