@ -73,18 +73,15 @@ static List *parsed_hba_lines = NIL;
static MemoryContext parsed_hba_context = NULL ;
/*
* These variables hold the pre - parsed contents of the ident usermap
* configuration file . ident_lines is a triple - nested list of lines , fields
* and tokens , as returned by tokenize_file . There will be one line in
* ident_lines for each ( non - empty , non - comment ) line of the file . Note there
* will always be at least one field , since blank lines are not entered in the
* data structure . ident_line_nums is an integer list containing the actual
* line number for each line represented in ident_lines . ident_context is
* the memory context holding all this .
* pre - parsed content of ident mapping file : list of IdentLine structs .
* parsed_ident_context is the memory context where it lives .
*
* NOTE : the IdentLine structs can contain pre - compiled regular expressions
* that live outside the memory context . Before destroying or resetting the
* memory context , they need to be expliticly free ' d .
*/
static List * ident_lines = NIL ;
static List * ident_line_nums = NIL ;
static MemoryContext ident_context = NULL ;
static List * parsed_ident_lines = NIL ;
static MemoryContext parsed_ident_context = NULL ;
static MemoryContext tokenize_file ( const char * filename , FILE * file ,
@ -782,8 +779,7 @@ check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
( errcode ( ERRCODE_CONFIG_FILE_ERROR ) , \
errmsg ( " missing entry in file \" %s \" at end of line %d " , \
IdentFileName , line_number ) ) ) ; \
* error_p = true ; \
return ; \
return NULL ; \
} \
} while ( 0 ) ;
@ -794,8 +790,7 @@ check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
errmsg ( " multiple values in ident field " ) , \
errcontext ( " line %d of configuration file \" %s \" " , \
line_number , IdentFileName ) ) ) ; \
* error_p = true ; \
return ; \
return NULL ; \
} \
} while ( 0 ) ;
@ -1794,34 +1789,37 @@ load_hba(void)
}
/*
* Process one line from the ident config file .
* Parse one tokenised line from the ident config file and store the result in
* an IdentLine structure , or NULL if parsing fails .
*
* Take the line and compare it to the needed map , pg_role and ident_user .
* * found_p and * error_p are set according to our results .
* The tokenised line is a nested List of fields and tokens .
*
* If ident_user is a regular expression ( ie . begins with a slash ) , it is
* compiled and stored in IdentLine structure .
*
* Note : this function leaks memory when an error occurs . Caller is expected
* to have set a memory context that will be reset if this function returns
* NULL .
*/
static void
parse_ident_usermap ( List * line , int line_number , const char * usermap_name ,
const char * pg_role , const char * ident_user ,
bool case_insensitive , bool * found_p , bool * error_p )
static IdentLine *
parse_ident_line ( List * line , int line_number )
{
ListCell * field ;
List * tokens ;
HbaToken * token ;
char * file_map ;
char * file_pgrole ;
char * file_ident_user ;
* found_p = false ;
* error_p = false ;
IdentLine * parsedline ;
Assert ( line ! = NIL ) ;
field = list_head ( line ) ;
parsedline = palloc0 ( sizeof ( IdentLine ) ) ;
parsedline - > linenumber = line_number ;
/* Get the map token (must exist) */
tokens = lfirst ( field ) ;
IDENT_MULTI_VALUE ( tokens ) ;
token = linitial ( tokens ) ;
file_ map = token - > string ;
parsedline - > user map = pstrdup ( token - > string ) ;
/* Get the ident user token */
field = lnext ( field ) ;
@ -1829,7 +1827,7 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name,
tokens = lfirst ( field ) ;
IDENT_MULTI_VALUE ( tokens ) ;
token = linitial ( tokens ) ;
file_ ident_user = token - > string ;
parsedline - > ident_user = pstrdup ( token - > string ) ;
/* Get the PG rolename token */
field = lnext ( field ) ;
@ -1837,57 +1835,80 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name,
tokens = lfirst ( field ) ;
IDENT_MULTI_VALUE ( tokens ) ;
token = linitial ( tokens ) ;
file_pgrole = token - > string ;
if ( strcmp ( file_map , usermap_name ) ! = 0 )
/* Line does not match the map name we're looking for, so just abort */
return ;
parsedline - > pg_role = pstrdup ( token - > string ) ;
/* Match? */
if ( file_ident_user [ 0 ] = = ' / ' )
if ( parsedline - > 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 .
* expression . Pre - compile it .
*/
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 ) ) ;
wstr = palloc ( ( strlen ( parsedline - > ident_user + 1 ) + 1 ) * sizeof ( pg_wchar ) ) ;
wlen = pg_mb2wchar_with_len ( parsedline - > ident_user + 1 ,
wstr , strlen ( parsedline - > 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 , C_COLLATION_OID ) ;
r = pg_regcomp ( & parsedline - > re , wstr , wlen , REG_ADVANCED , C_COLLATION_OID ) ;
if ( r )
{
char errstr [ 100 ] ;
pg_regerror ( r , & re , errstr , sizeof ( errstr ) ) ;
pg_regerror ( r , & parsedline - > re , errstr , sizeof ( errstr ) ) ;
ereport ( LOG ,
( errcode ( ERRCODE_INVALID_REGULAR_EXPRESSION ) ,
errmsg ( " invalid regular expression \" %s \" : %s " ,
file_ ident_user + 1 , errstr ) ) ) ;
parsedline - > ident_user + 1 , errstr ) ) ) ;
pfree ( wstr ) ;
* error_p = true ;
return ;
return NULL ;
}
pfree ( wstr ) ;
}
return parsedline ;
}
/*
* Process one line from the parsed ident config lines .
*
* Compare input parsed ident line to the needed map , pg_role and ident_user .
* * found_p and * error_p are set according to our results .
*/
static void
check_ident_usermap ( IdentLine * identLine , const char * usermap_name ,
const char * pg_role , const char * ident_user ,
bool case_insensitive , bool * found_p , bool * error_p )
{
* found_p = false ;
* error_p = false ;
if ( strcmp ( identLine - > usermap , usermap_name ) ! = 0 )
/* Line does not match the map name we're looking for, so just abort */
return ;
/* Match? */
if ( identLine - > 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 ;
regmatch_t matches [ 2 ] ;
pg_wchar * wstr ;
int wlen ;
char * ofs ;
char * regexp_pgrole ;
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 ) ;
r = pg_regexec ( & identLine - > re , wstr , wlen , 0 , NULL , 2 , matches , 0 ) ;
if ( r )
{
char errstr [ 100 ] ;
@ -1895,21 +1916,20 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name,
if ( r ! = REG_NOMATCH )
{
/* REG_NOMATCH is not an error, everything else is */
pg_regerror ( r , & re , errstr , sizeof ( errstr ) ) ;
pg_regerror ( r , & identLine - > re , errstr , sizeof ( errstr ) ) ;
ereport ( LOG ,
( errcode ( ERRCODE_INVALID_REGULAR_EXPRESSION ) ,
errmsg ( " regular expression match for \" %s \" failed: %s " ,
file_ ident_user + 1 , errstr ) ) ) ;
identLine - > ident_user + 1 , errstr ) ) ) ;
* error_p = true ;
}
pfree ( wstr ) ;
pg_regfree ( & re ) ;
return ;
}
pfree ( wstr ) ;
if ( ( ofs = strstr ( file_pg role, " \\ 1 " ) ) ! = NULL )
if ( ( ofs = strstr ( identLine - > pg_ role, " \\ 1 " ) ) ! = NULL )
{
/* substitution of the first argument requested */
if ( matches [ 1 ] . rm_so < 0 )
@ -1917,8 +1937,7 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name,
ereport ( LOG ,
( errcode ( ERRCODE_INVALID_REGULAR_EXPRESSION ) ,
errmsg ( " regular expression \" %s \" has no subexpressions as requested by backreference in \" %s \" " ,
file_ident_user + 1 , file_pgrole ) ) ) ;
pg_regfree ( & re ) ;
identLine - > ident_user + 1 , identLine - > pg_role ) ) ) ;
* error_p = true ;
return ;
}
@ -1927,8 +1946,8 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name,
* length : original length minus length of \ 1 plus length of match
* plus null terminator
*/
regexp_pgrole = palloc0 ( strlen ( file_pg role) - 2 + ( matches [ 1 ] . rm_eo - matches [ 1 ] . rm_so ) + 1 ) ;
strncpy ( regexp_pgrole , file_pg role, ( ofs - file_pg role) ) ;
regexp_pgrole = palloc0 ( strlen ( identLine - > pg_ role) - 2 + ( matches [ 1 ] . rm_eo - matches [ 1 ] . rm_so ) + 1 ) ;
strncpy ( regexp_pgrole , identLine - > pg_ role, ( ofs - identLine - > pg_ role) ) ;
memcpy ( regexp_pgrole + strlen ( regexp_pgrole ) ,
ident_user + matches [ 1 ] . rm_so ,
matches [ 1 ] . rm_eo - matches [ 1 ] . rm_so ) ;
@ -1937,11 +1956,9 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name,
else
{
/* no substitution, so copy the match */
regexp_pgrole = pstrdup ( file_pg role) ;
regexp_pgrole = pstrdup ( identLine - > pg_ role) ;
}
pg_regfree ( & re ) ;
/*
* now check if the username actually matched what the user is trying
* to connect as
@ -1965,14 +1982,14 @@ parse_ident_usermap(List *line, int line_number, const char *usermap_name,
/* Not regular expression, so make complete match */
if ( case_insensitive )
{
if ( pg_strcasecmp ( file_pg role, pg_role ) = = 0 & &
pg_strcasecmp ( file_ ident_user, ident_user ) = = 0 )
if ( pg_strcasecmp ( identLine - > pg_ role, pg_role ) = = 0 & &
pg_strcasecmp ( identLine - > ident_user , ident_user ) = = 0 )
* found_p = true ;
}
else
{
if ( strcmp ( file_pg role, pg_role ) = = 0 & &
strcmp ( file_ ident_user, ident_user ) = = 0 )
if ( strcmp ( identLine - > pg_ role, pg_role ) = = 0 & &
strcmp ( identLine - > ident_user , ident_user ) = = 0 )
* found_p = true ;
}
}
@ -2021,13 +2038,12 @@ check_usermap(const char *usermap_name,
}
else
{
ListCell * line_cell ,
* num_cell ;
ListCell * line_cell ;
forbot h ( line_cell , ident_lines , num_cell , ident_line_num s)
foreac h ( line_cell , parsed_ident_line s)
{
parse _ident_usermap( lfirst ( line_cell ) , lfirst_int ( num_cell ) ,
usermap_name , pg_role , auth_user , case_insensitive ,
check _ident_usermap( lfirst ( line_cell ) , usermap_name ,
pg_role , auth_user , case_insensitive ,
& found_entry , & error ) ;
if ( found_entry | | error )
break ;
@ -2044,21 +2060,26 @@ check_usermap(const char *usermap_name,
/*
* Read the ident config file and populate ident_lines and ident_line_nums .
* Read the ident config file and create a List of IdentLine records for
* the contents .
*
* Like parsed_hba_lines , ident_lines is a triple - nested List of lines , fields
* and tokens .
* This works the same as load_hba ( ) , but for the user config file .
*/
void
bool
load_ident ( void )
{
FILE * file ;
if ( ident_context ! = NULL )
{
MemoryContextDelete ( ident_context ) ;
ident_context = NULL ;
}
List * ident_lines = NIL ;
List * ident_line_nums = NIL ;
ListCell * line_cell ,
* num_cell ,
* parsed_line_cell ;
List * new_parsed_lines = NIL ;
bool ok = true ;
MemoryContext linecxt ;
MemoryContext oldcxt ;
MemoryContext ident_context ;
IdentLine * newline ;
file = AllocateFile ( IdentFileName , " r " ) ;
if ( file = = NULL )
@ -2068,13 +2089,80 @@ load_ident(void)
( errcode_for_file_access ( ) ,
errmsg ( " could not open usermap file \" %s \" : %m " ,
IdentFileName ) ) ) ;
return false ;
}
else
linecxt = tokenize_file ( IdentFileName , file , & ident_lines , & ident_line_nums ) ;
FreeFile ( file ) ;
/* Now parse all the lines */
ident_context = AllocSetContextCreate ( TopMemoryContext ,
" ident parser context " ,
ALLOCSET_DEFAULT_MINSIZE ,
ALLOCSET_DEFAULT_MINSIZE ,
ALLOCSET_DEFAULT_MAXSIZE ) ;
oldcxt = MemoryContextSwitchTo ( ident_context ) ;
forboth ( line_cell , ident_lines , num_cell , ident_line_nums )
{
ident_context = tokenize_file ( IdentFileName , file , & ident_lines ,
& ident_line_nums ) ;
FreeFile ( file ) ;
if ( ( newline = parse_ident_line ( lfirst ( line_cell ) , lfirst_int ( num_cell ) ) ) = = NULL )
{
/*
* Parse error in the file , so indicate there ' s a problem . Free
* all the memory and regular expressions of lines parsed so far .
*/
foreach ( parsed_line_cell , new_parsed_lines )
{
newline = ( IdentLine * ) lfirst ( parsed_line_cell ) ;
if ( newline - > ident_user [ 0 ] = = ' / ' )
pg_regfree ( & newline - > re ) ;
}
MemoryContextReset ( ident_context ) ;
new_parsed_lines = NIL ;
ok = false ;
/*
* Keep parsing the rest of the file so we can report errors on
* more than the first row . Error has already been reported in the
* parsing function , so no need to log it here .
*/
continue ;
}
new_parsed_lines = lappend ( new_parsed_lines , newline ) ;
}
/* Free tokenizer memory */
MemoryContextDelete ( linecxt ) ;
MemoryContextSwitchTo ( oldcxt ) ;
if ( ! ok )
{
/* File contained one or more errors, so bail out */
foreach ( parsed_line_cell , new_parsed_lines )
{
newline = ( IdentLine * ) lfirst ( parsed_line_cell ) ;
if ( newline - > ident_user [ 0 ] = = ' / ' )
pg_regfree ( & newline - > re ) ;
}
MemoryContextDelete ( ident_context ) ;
return false ;
}
/* Loaded new file successfully, replace the one we use */
if ( parsed_ident_lines ! = NULL )
{
foreach ( parsed_line_cell , parsed_ident_lines )
{
newline = ( IdentLine * ) lfirst ( parsed_line_cell ) ;
if ( newline - > ident_user [ 0 ] = = ' / ' )
pg_regfree ( & newline - > re ) ;
}
MemoryContextDelete ( parsed_ident_context ) ;
}
parsed_ident_context = ident_context ;
parsed_ident_lines = new_parsed_lines ;
return true ;
}