@@ -66,6 +66,7 @@ typedef struct check_network_data
6666} check_network_data ;
6767
6868
69+ #define token_has_regexp (t ) (t->regex != NULL)
6970#define token_is_keyword (t , k ) (!t->quoted && strcmp(t->string, k) == 0)
7071#define token_matches (t , k ) (strcmp(t->string, k) == 0)
7172
@@ -80,9 +81,10 @@ static MemoryContext parsed_hba_context = NULL;
8081 * pre-parsed content of ident mapping file: list of IdentLine structs.
8182 * parsed_ident_context is the memory context where it lives.
8283 *
83- * NOTE: the IdentLine structs can contain pre-compiled regular expressions
84- * that live outside the memory context. Before destroying or resetting the
85- * memory context, they need to be explicitly free'd.
84+ * NOTE: the IdentLine structs can contain AuthTokens with pre-compiled
85+ * regular expressions that live outside the memory context. Before
86+ * destroying or resetting the memory context, they need to be explicitly
87+ * free'd.
8688 */
8789static List * parsed_ident_lines = NIL ;
8890static MemoryContext parsed_ident_context = NULL ;
@@ -117,6 +119,9 @@ static List *tokenize_inc_file(List *tokens, const char *outer_filename,
117119 const char * inc_filename , int elevel , char * * err_msg );
118120static bool parse_hba_auth_opt (char * name , char * val , HbaLine * hbaline ,
119121 int elevel , char * * err_msg );
122+ static int regcomp_auth_token (AuthToken * token );
123+ static int regexec_auth_token (const char * match , AuthToken * token ,
124+ size_t nmatch , regmatch_t pmatch []);
120125
121126
122127/*
@@ -267,14 +272,26 @@ make_auth_token(const char *token, bool quoted)
267272
268273 toklen = strlen (token );
269274 /* we copy string into same palloc block as the struct */
270- authtoken = (AuthToken * ) palloc (sizeof (AuthToken ) + toklen + 1 );
275+ authtoken = (AuthToken * ) palloc0 (sizeof (AuthToken ) + toklen + 1 );
271276 authtoken -> string = (char * ) authtoken + sizeof (AuthToken );
272277 authtoken -> quoted = quoted ;
278+ authtoken -> regex = NULL ;
273279 memcpy (authtoken -> string , token , toklen + 1 );
274280
275281 return authtoken ;
276282}
277283
284+ /*
285+ * Free an AuthToken, that may include a regular expression that needs
286+ * to be cleaned up explicitly.
287+ */
288+ static void
289+ free_auth_token (AuthToken * token )
290+ {
291+ if (token_has_regexp (token ))
292+ pg_regfree (token -> regex );
293+ }
294+
278295/*
279296 * Copy a AuthToken struct into freshly palloc'd memory.
280297 */
@@ -286,6 +303,56 @@ copy_auth_token(AuthToken *in)
286303 return out ;
287304}
288305
306+ /*
307+ * Compile the regular expression and store it in the AuthToken given in
308+ * input. Returns the result of pg_regcomp().
309+ */
310+ static int
311+ regcomp_auth_token (AuthToken * token )
312+ {
313+ pg_wchar * wstr ;
314+ int wlen ;
315+ int rc ;
316+
317+ Assert (token -> regex == NULL );
318+
319+ if (token -> string [0 ] != '/' )
320+ return 0 ; /* nothing to compile */
321+
322+ token -> regex = (regex_t * ) palloc0 (sizeof (regex_t ));
323+ wstr = palloc ((strlen (token -> string + 1 ) + 1 ) * sizeof (pg_wchar ));
324+ wlen = pg_mb2wchar_with_len (token -> string + 1 ,
325+ wstr , strlen (token -> string + 1 ));
326+
327+ rc = pg_regcomp (token -> regex , wstr , wlen , REG_ADVANCED , C_COLLATION_OID );
328+
329+ pfree (wstr );
330+ return rc ;
331+ }
332+
333+ /*
334+ * Execute a regular expression computed in an AuthToken, checking for a match
335+ * with the string specified in "match". The caller may optionally give an
336+ * array to store the matches. Returns the result of pg_regexec().
337+ */
338+ static int
339+ regexec_auth_token (const char * match , AuthToken * token , size_t nmatch ,
340+ regmatch_t pmatch [])
341+ {
342+ pg_wchar * wmatchstr ;
343+ int wmatchlen ;
344+ int r ;
345+
346+ Assert (token -> string [0 ] == '/' && token -> regex );
347+
348+ wmatchstr = palloc ((strlen (match ) + 1 ) * sizeof (pg_wchar ));
349+ wmatchlen = pg_mb2wchar_with_len (match , wmatchstr , strlen (match ));
350+
351+ r = pg_regexec (token -> regex , wmatchstr , wmatchlen , 0 , NULL , nmatch , pmatch , 0 );
352+
353+ pfree (wmatchstr );
354+ return r ;
355+ }
289356
290357/*
291358 * Tokenize one HBA field from a line, handling file inclusion and comma lists.
@@ -2307,6 +2374,7 @@ parse_ident_line(TokenizedAuthLine *tok_line, int elevel)
23072374 List * tokens ;
23082375 AuthToken * token ;
23092376 IdentLine * parsedline ;
2377+ int rc ;
23102378
23112379 Assert (tok_line -> fields != NIL );
23122380 field = list_head (tok_line -> fields );
@@ -2326,7 +2394,9 @@ parse_ident_line(TokenizedAuthLine *tok_line, int elevel)
23262394 tokens = lfirst (field );
23272395 IDENT_MULTI_VALUE (tokens );
23282396 token = linitial (tokens );
2329- parsedline -> ident_user = pstrdup (token -> string );
2397+
2398+ /* Copy the ident user token */
2399+ parsedline -> token = copy_auth_token (token );
23302400
23312401 /* Get the PG rolename token */
23322402 field = lnext (tok_line -> fields , field );
@@ -2336,40 +2406,27 @@ parse_ident_line(TokenizedAuthLine *tok_line, int elevel)
23362406 token = linitial (tokens );
23372407 parsedline -> pg_role = pstrdup (token -> string );
23382408
2339- if (parsedline -> ident_user [0 ] == '/' )
2409+ /*
2410+ * Now that the field validation is done, compile a regex from the user
2411+ * token, if necessary.
2412+ */
2413+ rc = regcomp_auth_token (parsedline -> token );
2414+ if (rc )
23402415 {
2341- /*
2342- * When system username starts with a slash, treat it as a regular
2343- * expression. Pre-compile it.
2344- */
2345- int r ;
2346- pg_wchar * wstr ;
2347- int wlen ;
2348-
2349- wstr = palloc ((strlen (parsedline -> ident_user + 1 ) + 1 ) * sizeof (pg_wchar ));
2350- wlen = pg_mb2wchar_with_len (parsedline -> ident_user + 1 ,
2351- wstr , strlen (parsedline -> ident_user + 1 ));
2352-
2353- r = pg_regcomp (& parsedline -> re , wstr , wlen , REG_ADVANCED , C_COLLATION_OID );
2354- if (r )
2355- {
2356- char errstr [100 ];
2416+ char errstr [100 ];
23572417
2358- pg_regerror (r , & parsedline -> re , errstr , sizeof (errstr ));
2359- ereport (elevel ,
2360- (errcode (ERRCODE_INVALID_REGULAR_EXPRESSION ),
2361- errmsg ("invalid regular expression \"%s\": %s" ,
2362- parsedline -> ident_user + 1 , errstr ),
2363- errcontext ("line %d of configuration file \"%s\"" ,
2418+ pg_regerror (rc , parsedline -> token -> regex , errstr , sizeof (errstr ));
2419+ ereport (elevel ,
2420+ (errcode (ERRCODE_INVALID_REGULAR_EXPRESSION ),
2421+ errmsg ("invalid regular expression \"%s\": %s" ,
2422+ parsedline -> token -> string + 1 , errstr ),
2423+ errcontext ("line %d of configuration file \"%s\"" ,
23642424 line_num , IdentFileName )));
23652425
2366- * err_msg = psprintf ("invalid regular expression \"%s\": %s" ,
2367- parsedline -> ident_user + 1 , errstr );
2426+ * err_msg = psprintf ("invalid regular expression \"%s\": %s" ,
2427+ parsedline -> token -> string + 1 , errstr );
23682428
2369- pfree (wstr );
2370- return NULL ;
2371- }
2372- pfree (wstr );
2429+ return NULL ;
23732430 }
23742431
23752432 return parsedline ;
@@ -2394,44 +2451,35 @@ check_ident_usermap(IdentLine *identLine, const char *usermap_name,
23942451 return ;
23952452
23962453 /* Match? */
2397- if (identLine -> ident_user [ 0 ] == '/' )
2454+ if (token_has_regexp ( identLine -> token ) )
23982455 {
23992456 /*
2400- * When system username starts with a slash, treat it as a regular
2401- * expression. In this case, we process the system username as a
2402- * regular expression that returns exactly one match. This is replaced
2403- * for \1 in the database username string, if present.
2457+ * Process the system username as a regular expression that returns
2458+ * exactly one match. This is replaced for \1 in the database username
2459+ * string, if present.
24042460 */
24052461 int r ;
24062462 regmatch_t matches [2 ];
2407- pg_wchar * wstr ;
2408- int wlen ;
24092463 char * ofs ;
24102464 char * regexp_pgrole ;
24112465
2412- wstr = palloc ((strlen (ident_user ) + 1 ) * sizeof (pg_wchar ));
2413- wlen = pg_mb2wchar_with_len (ident_user , wstr , strlen (ident_user ));
2414-
2415- r = pg_regexec (& identLine -> re , wstr , wlen , 0 , NULL , 2 , matches , 0 );
2466+ r = regexec_auth_token (ident_user , identLine -> token , 2 , matches );
24162467 if (r )
24172468 {
24182469 char errstr [100 ];
24192470
24202471 if (r != REG_NOMATCH )
24212472 {
24222473 /* REG_NOMATCH is not an error, everything else is */
2423- pg_regerror (r , & identLine -> re , errstr , sizeof (errstr ));
2474+ pg_regerror (r , identLine -> token -> regex , errstr , sizeof (errstr ));
24242475 ereport (LOG ,
24252476 (errcode (ERRCODE_INVALID_REGULAR_EXPRESSION ),
24262477 errmsg ("regular expression match for \"%s\" failed: %s" ,
2427- identLine -> ident_user + 1 , errstr )));
2478+ identLine -> token -> string + 1 , errstr )));
24282479 * error_p = true;
24292480 }
2430-
2431- pfree (wstr );
24322481 return ;
24332482 }
2434- pfree (wstr );
24352483
24362484 if ((ofs = strstr (identLine -> pg_role , "\\1" )) != NULL )
24372485 {
@@ -2443,7 +2491,7 @@ check_ident_usermap(IdentLine *identLine, const char *usermap_name,
24432491 ereport (LOG ,
24442492 (errcode (ERRCODE_INVALID_REGULAR_EXPRESSION ),
24452493 errmsg ("regular expression \"%s\" has no subexpressions as requested by backreference in \"%s\"" ,
2446- identLine -> ident_user + 1 , identLine -> pg_role )));
2494+ identLine -> token -> string + 1 , identLine -> pg_role )));
24472495 * error_p = true;
24482496 return ;
24492497 }
@@ -2490,13 +2538,13 @@ check_ident_usermap(IdentLine *identLine, const char *usermap_name,
24902538 if (case_insensitive )
24912539 {
24922540 if (pg_strcasecmp (identLine -> pg_role , pg_role ) == 0 &&
2493- pg_strcasecmp (identLine -> ident_user , ident_user ) == 0 )
2541+ pg_strcasecmp (identLine -> token -> string , ident_user ) == 0 )
24942542 * found_p = true;
24952543 }
24962544 else
24972545 {
24982546 if (strcmp (identLine -> pg_role , pg_role ) == 0 &&
2499- strcmp (identLine -> ident_user , ident_user ) == 0 )
2547+ strcmp (identLine -> token -> string , ident_user ) == 0 )
25002548 * found_p = true;
25012549 }
25022550 }
@@ -2646,8 +2694,7 @@ load_ident(void)
26462694 foreach (parsed_line_cell , new_parsed_lines )
26472695 {
26482696 newline = (IdentLine * ) lfirst (parsed_line_cell );
2649- if (newline -> ident_user [0 ] == '/' )
2650- pg_regfree (& newline -> re );
2697+ free_auth_token (newline -> token );
26512698 }
26522699 MemoryContextDelete (ident_context );
26532700 return false;
@@ -2659,8 +2706,7 @@ load_ident(void)
26592706 foreach (parsed_line_cell , parsed_ident_lines )
26602707 {
26612708 newline = (IdentLine * ) lfirst (parsed_line_cell );
2662- if (newline -> ident_user [0 ] == '/' )
2663- pg_regfree (& newline -> re );
2709+ free_auth_token (newline -> token );
26642710 }
26652711 }
26662712 if (parsed_ident_context != NULL )
0 commit comments