From b2000c98156dc85de08db32b9b71a2f6a4528f1d Mon Sep 17 00:00:00 2001 From: Julien Rouhaud Date: Mon, 21 Feb 2022 15:45:26 +0800 Subject: [PATCH v2 3/4] Allow file inclusion in pg_hba and pg_ident files. Author: Julien Rouhaud Reviewed-by: FIXME Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya%40jrouhaud --- doc/src/sgml/catalogs.sgml | 48 +++- doc/src/sgml/client-auth.sgml | 34 ++- src/backend/libpq/hba.c | 373 ++++++++++++++++--------- src/backend/libpq/pg_hba.conf.sample | 8 +- src/backend/libpq/pg_ident.conf.sample | 8 +- src/backend/utils/adt/hbafuncs.c | 53 +++- src/include/catalog/pg_proc.dat | 12 +- src/include/libpq/hba.h | 2 + src/test/regress/expected/rules.out | 12 +- 9 files changed, 395 insertions(+), 155 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 0de40a9626..07c6679a52 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -10430,12 +10430,31 @@ SCRAM-SHA-256$<iteration count>:&l + + + rule_number int4 + + + Rule number, in priority order, of this rule if the rule is valid, + otherwise null + + + + + + file_name text + + + File name of this rule + + + line_number int4 - Line number of this rule in pg_hba.conf + Line number of this rule in the given file_name @@ -10571,6 +10590,33 @@ SCRAM-SHA-256$<iteration count>:&l + + + mapping_number int4 + + + Rule number, in priority order, of this mapping if the mapping is valid, + otherwise null + + + + + + file_name text + + + File name of this mapping + + + + + + line_number int4 + + + Line number of this mapping in the given file_name + + line_number int4 diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index 142b0affcb..e1d0e103b3 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -89,8 +89,17 @@ - Each record specifies a connection type, a client IP address range - (if relevant for the connection type), a database name, a user name, + Each record can either be an inclusion directive or an authentication rule. + Inclusion records specifies files that can be included, which contains + additional records. The records will be inserted in lieu of the inclusion + records. Those records only contains two fields: the + include directive and the file to be included. The file + can be a relative of absolute path, and can be double quoted if needed. + + + + Each authentication record specifies a connection type, a client IP address + range (if relevant for the connection type), a database name, a user name, and the authentication method to be used for connections matching these parameters. The first record with a matching connection type, client address, requested database, and user name is used to perform @@ -103,6 +112,7 @@ A record can have several formats: +include file local database user auth-method auth-options host database user address auth-method auth-options hostssl database user address auth-method auth-options @@ -118,6 +128,15 @@ hostnogssenc database user + + include + + + This line will be replaced with the content of the given file. + + + + local @@ -835,8 +854,9 @@ local db1,db2,@demodbs all md5 cluster's data directory. (It is possible to place the map file elsewhere, however; see the configuration parameter.) - The ident map file contains lines of the general form: + The ident map file contains lines of two general form: +include file map-name system-username database-username Comments, whitespace and line continuations are handled in the same way as in @@ -847,6 +867,14 @@ local db1,db2,@demodbs all md5 database user name. The same map-name can be used repeatedly to specify multiple user-mappings within a single map. + + The lines can record can either be an inclusion directive or an authentication rule. + Inclusion records specifies files that can be included, which contains + additional records. The records will be inserted in lieu of the inclusion + records. Those records only contains two fields: the + include directive and the file to be included. The file + can be a relative of absolute path, and can be double quoted if needed. + There is no restriction regarding how many database users a given operating system user can correspond to, nor vice versa. Thus, entries diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index e673ff5474..85988fdeda 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -68,6 +68,12 @@ typedef struct check_network_data #define token_is_keyword(t, k) (!t->quoted && strcmp(t->string, k) == 0) #define token_matches(t, k) (strcmp(t->string, k) == 0) +typedef enum HbaIncludeKind +{ + SecondaryAuthFile, + IncludedAuthFile +} HbaIncludeKind; + /* * pre-parsed content of HBA config file: list of HbaLine structs. * parsed_hba_context is the memory context where it lives. @@ -112,6 +118,12 @@ static const char *const UserAuthName[] = }; +static FILE *open_inc_file(HbaIncludeKind kind, const char *inc_filename, + const char *outer_filename, int elevel, + char **err_msg, char **inc_fullname); +static void tokenize_auth_file_with_context(MemoryContext Linecxt, + const char *filename, FILE *file, + List **tok_lines, int elevel); static List *tokenize_inc_file(List *tokens, const char *outer_filename, const char *inc_filename, int elevel, char **err_msg); static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, @@ -327,6 +339,68 @@ next_field_expand(const char *filename, char **lineptr, return tokens; } +/* + * Open the given file for inclusion in an authentication file, whether + * secondary or included. +*/ +static FILE * +open_inc_file(HbaIncludeKind kind, const char *inc_filename, + const char *outer_filename, int elevel, char **err_msg, + char **inc_fullname) +{ + FILE *inc_file; + + if (is_absolute_path(inc_filename)) + { + /* absolute path is taken as-is */ + *inc_fullname = pstrdup(inc_filename); + } + else + { + /* relative path is relative to dir of calling file */ + *inc_fullname = (char *) palloc(strlen(outer_filename) + 1 + + strlen(inc_filename) + 1); + strcpy(*inc_fullname, outer_filename); + get_parent_directory(*inc_fullname); + join_path_components(*inc_fullname, *inc_fullname, inc_filename); + canonicalize_path(*inc_fullname); + } + + inc_file = AllocateFile(*inc_fullname, "r"); + if (inc_file == NULL) + { + int save_errno = errno; + const char *msglog; + const char *msgview; + + switch (kind) + { + case SecondaryAuthFile: + msglog = "could not open secondary authentication file \"@%s\" as \"%s\": %m"; + msgview = "could not open secondary authentication file \"@%s\" as \"%s\": %s"; + break; + case IncludedAuthFile: + msglog = "could not open included authentication file \"%s\" as \"%s\": %m"; + msgview = "could not open included authentication file \"%s\" as \"%s\": %s"; + break; + default: + elog(ERROR, "unknown HbaIncludeKind: %d", kind); + break; + } + + ereport(elevel, + (errcode_for_file_access(), + errmsg(msglog, inc_filename, *inc_fullname))); + *err_msg = psprintf(msgview, inc_filename, *inc_fullname, + strerror(save_errno)); + pfree(*inc_fullname); + *inc_fullname = NULL; + return NULL; + } + + return inc_file; +} + /* * tokenize_inc_file * Expand a file included from another file into an hba "field" @@ -355,36 +429,11 @@ tokenize_inc_file(List *tokens, ListCell *inc_line; MemoryContext linecxt; - if (is_absolute_path(inc_filename)) - { - /* absolute path is taken as-is */ - inc_fullname = pstrdup(inc_filename); - } - else - { - /* relative path is relative to dir of calling file */ - inc_fullname = (char *) palloc(strlen(outer_filename) + 1 + - strlen(inc_filename) + 1); - strcpy(inc_fullname, outer_filename); - get_parent_directory(inc_fullname); - join_path_components(inc_fullname, inc_fullname, inc_filename); - canonicalize_path(inc_fullname); - } + inc_file = open_inc_file(SecondaryAuthFile, inc_filename, outer_filename, + elevel, err_msg, &inc_fullname); - inc_file = AllocateFile(inc_fullname, "r"); if (inc_file == NULL) - { - int save_errno = errno; - - ereport(elevel, - (errcode_for_file_access(), - errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m", - inc_filename, inc_fullname))); - *err_msg = psprintf("could not open secondary authentication file \"@%s\" as \"%s\": %s", - inc_filename, inc_fullname, strerror(save_errno)); - pfree(inc_fullname); return tokens; - } /* There is possible recursion here if the file contains @ */ linecxt = tokenize_auth_file(inc_fullname, inc_file, &inc_lines, elevel); @@ -423,6 +472,169 @@ tokenize_inc_file(List *tokens, return tokens; } +/* + * Tokenize the given file. + * + * The output is a list of TokenizedAuthLine structs; see struct definition + * above. + * + * linecxt: memory context which must contain all memory allocated by the + * function + * filename: the absolute path to the target file + * file: the already-opened target file + * tok_lines: receives output list + * elevel: message logging level + * + * Errors are reported by logging messages at ereport level elevel and by + * adding TokenizedAuthLine structs containing non-null err_msg fields to the + * output list. + * + */ +static void +tokenize_auth_file_with_context(MemoryContext linecxt, const char *filename, + FILE *file, List **tok_lines, int elevel) +{ + StringInfoData buf; + int line_number = 1; + MemoryContext oldcxt; + + oldcxt = MemoryContextSwitchTo(linecxt); + + initStringInfo(&buf); + + while (!feof(file) && !ferror(file)) + { + TokenizedAuthLine *tok_line; + char *lineptr; + List *current_line = NIL; + char *err_msg = NULL; + int last_backslash_buflen = 0; + int continuations = 0; + + /* Collect the next input line, handling backslash continuations */ + resetStringInfo(&buf); + + while (pg_get_line_append(file, &buf, NULL)) + { + /* Strip trailing newline, including \r in case we're on Windows */ + buf.len = pg_strip_crlf(buf.data); + + /* + * Check for backslash continuation. The backslash must be after + * the last place we found a continuation, else two backslashes + * followed by two \n's would behave surprisingly. + */ + if (buf.len > last_backslash_buflen && + buf.data[buf.len - 1] == '\\') + { + /* Continuation, so strip it and keep reading */ + buf.data[--buf.len] = '\0'; + last_backslash_buflen = buf.len; + continuations++; + continue; + } + + /* Nope, so we have the whole line */ + break; + } + + if (ferror(file)) + { + /* I/O error! */ + int save_errno = errno; + + ereport(elevel, + (errcode_for_file_access(), + errmsg("could not read file \"%s\": %m", filename))); + err_msg = psprintf("could not read file \"%s\": %s", + filename, strerror(save_errno)); + break; + } + + /* Parse fields */ + lineptr = buf.data; + while (*lineptr && err_msg == NULL) + { + List *current_field; + + current_field = next_field_expand(filename, &lineptr, + elevel, &err_msg); + /* add field to line, unless we are at EOL or comment start */ + if (current_field != NIL) + current_line = lappend(current_line, current_field); + } + + /* + * Reached EOL; no need to emit line to TokenizedAuthLine list if it's + * boring. + */ + if (current_line == NIL && err_msg == NULL) + goto next_line; + + /* If the line is valid, check if that's an include directive */ + if (err_msg == NULL && list_length(current_line) == 2) + { + HbaToken *first, *second; + + first = linitial(linitial_node(List, current_line)); + second = linitial(lsecond_node(List, current_line)); + + if (strcmp(first->string, "include") == 0) + { + char *inc_filename; + char *inc_fullname; + FILE *inc_file; + + inc_filename = second->string; + + inc_file = open_inc_file(IncludedAuthFile, inc_filename, + filename, elevel, &err_msg, + &inc_fullname); + + /* + * The included file could be open, now recursively process it. + * Errors will be reported in the general TokenizedAuthLine + * processing. + */ + if (inc_file != NULL) + { + tokenize_auth_file_with_context(linecxt, inc_fullname, + inc_file, tok_lines, + elevel); + + FreeFile(inc_file); + pfree(inc_fullname); + + /* + * The line is fulle processed, bypass the general + * TokenizedAuthLine processing. + */ + goto next_line; + } + else + { + /* We should got an error */ + Assert(err_msg != NULL); + } + } + } + + /* General processing: Emit line to the TokenizedAuthLine */ + tok_line = (TokenizedAuthLine *) palloc(sizeof(TokenizedAuthLine)); + tok_line->fields = current_line; + tok_line->file_name = pstrdup(filename); + tok_line->line_num = line_number; + tok_line->raw_line = pstrdup(buf.data); + tok_line->err_msg = err_msg; + *tok_lines = lappend(*tok_lines, tok_line); + +next_line: + line_number += continuations + 1; + + } + + MemoryContextSwitchTo(oldcxt); +} /* * Does user belong to role? @@ -1796,6 +2008,7 @@ parse_hba_line(TokenizedAuthLine *tok_line, int elevel) HbaLine *parsedline; parsedline = palloc0(sizeof(HbaLine)); + parsedline->sourcefile = pstrdup(tok_line->file_name); parsedline->linenumber = line_num; parsedline->rawline = pstrdup(tok_line->raw_line); @@ -2602,118 +2815,26 @@ parse_ident_line(TokenizedAuthLine *tok_line, int elevel) /* * Tokenize the given file. * - * The output is a list of TokenizedAuthLine structs; see struct definition - * above. + * Wrapper around tokenize_auth_file_with_context, creating a dedicated memory + * context. * - * filename: the absolute path to the target file - * file: the already-opened target file - * tok_lines: receives output list - * elevel: message logging level - * - * Errors are reported by logging messages at ereport level elevel and by - * adding TokenizedAuthLine structs containing non-null err_msg fields to the - * output list. - * - * Return value is a memory context which contains all memory allocated by + * Return value is this memory context which contains all memory allocated by * this function (it's a child of caller's context). */ MemoryContext -tokenize_auth_file(const char *filename, FILE *file, List **tok_lines, int elevel) +tokenize_auth_file(const char *filename, FILE *file, List **tok_lines, + int elevel) { - int line_number = 1; - StringInfoData buf; MemoryContext linecxt; - MemoryContext oldcxt; linecxt = AllocSetContextCreate(CurrentMemoryContext, "tokenize_auth_file", ALLOCSET_SMALL_SIZES); - oldcxt = MemoryContextSwitchTo(linecxt); - - initStringInfo(&buf); *tok_lines = NIL; - while (!feof(file) && !ferror(file)) - { - char *lineptr; - List *current_line = NIL; - char *err_msg = NULL; - int last_backslash_buflen = 0; - int continuations = 0; - - /* Collect the next input line, handling backslash continuations */ - resetStringInfo(&buf); - - while (pg_get_line_append(file, &buf, NULL)) - { - /* Strip trailing newline, including \r in case we're on Windows */ - buf.len = pg_strip_crlf(buf.data); - - /* - * Check for backslash continuation. The backslash must be after - * the last place we found a continuation, else two backslashes - * followed by two \n's would behave surprisingly. - */ - if (buf.len > last_backslash_buflen && - buf.data[buf.len - 1] == '\\') - { - /* Continuation, so strip it and keep reading */ - buf.data[--buf.len] = '\0'; - last_backslash_buflen = buf.len; - continuations++; - continue; - } - - /* Nope, so we have the whole line */ - break; - } - - if (ferror(file)) - { - /* I/O error! */ - int save_errno = errno; - - ereport(elevel, - (errcode_for_file_access(), - errmsg("could not read file \"%s\": %m", filename))); - err_msg = psprintf("could not read file \"%s\": %s", - filename, strerror(save_errno)); - break; - } - - /* Parse fields */ - lineptr = buf.data; - while (*lineptr && err_msg == NULL) - { - List *current_field; - - current_field = next_field_expand(filename, &lineptr, - elevel, &err_msg); - /* add field to line, unless we are at EOL or comment start */ - if (current_field != NIL) - current_line = lappend(current_line, current_field); - } - - /* - * Reached EOL; emit line to TokenizedAuthLine list unless it's boring - * */ - if (current_line != NIL || err_msg != NULL) - { - TokenizedAuthLine *tok_line; - - tok_line = (TokenizedAuthLine *) palloc(sizeof(TokenizedAuthLine)); - tok_line->fields = current_line; - tok_line->line_num = line_number; - tok_line->raw_line = pstrdup(buf.data); - tok_line->err_msg = err_msg; - *tok_lines = lappend(*tok_lines, tok_line); - } - - line_number += continuations + 1; - } - - MemoryContextSwitchTo(oldcxt); + tokenize_auth_file_with_context(linecxt, filename, file, tok_lines, + elevel); return linecxt; } diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample index 5f3f63eb0c..0b6589a7b9 100644 --- a/src/backend/libpq/pg_hba.conf.sample +++ b/src/backend/libpq/pg_hba.conf.sample @@ -9,6 +9,7 @@ # are authenticated, which PostgreSQL user names they can use, which # databases they can access. Records take one of these forms: # +# include FILE # local DATABASE USER METHOD [OPTIONS] # host DATABASE USER ADDRESS METHOD [OPTIONS] # hostssl DATABASE USER ADDRESS METHOD [OPTIONS] @@ -18,7 +19,12 @@ # # (The uppercase items must be replaced by actual values.) # -# The first field is the connection type: +# If the first field is "include", it's not a mapping record but a directive to +# include records from another file, specified in the field. FILE is the file +# to include. It can be specified with a relative or absolute path, and can be +# double quoted if it contains spaces. +# +# Otherwise the first field is the connection type: # - "local" is a Unix-domain socket # - "host" is a TCP/IP socket (encrypted or not) # - "hostssl" is a TCP/IP socket that is SSL-encrypted diff --git a/src/backend/libpq/pg_ident.conf.sample b/src/backend/libpq/pg_ident.conf.sample index a5870e6448..138359cf03 100644 --- a/src/backend/libpq/pg_ident.conf.sample +++ b/src/backend/libpq/pg_ident.conf.sample @@ -7,12 +7,18 @@ # # This file controls PostgreSQL user name mapping. It maps external # user names to their corresponding PostgreSQL user names. Records -# are of the form: +# are one of these forms: # +# include FILE # MAPNAME SYSTEM-USERNAME PG-USERNAME # # (The uppercase quantities must be replaced by actual values.) # +# If the first field is "include", it's not an authentication record but a +# directive to include records from another file, specified in the field. FILE +# is the file to include. It can be specified with a relative or absolute +# path, and can be double quoted if it contains spaces. +# # MAPNAME is the (otherwise freely chosen) map name that was used in # pg_hba.conf. SYSTEM-USERNAME is the detected user name of the # client. PG-USERNAME is the requested PostgreSQL user name. The diff --git a/src/backend/utils/adt/hbafuncs.c b/src/backend/utils/adt/hbafuncs.c index 2189c298b8..4c9df29427 100644 --- a/src/backend/utils/adt/hbafuncs.c +++ b/src/backend/utils/adt/hbafuncs.c @@ -26,16 +26,18 @@ static void fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc, - int lineno, HbaLine *hba, const char *err_msg); + int tule_number, const char *filename, int lineno, + HbaLine *hba, const char *err_msg); static void fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc); static void fill_ident_line(Tuplestorestate *tuple_store, TupleDesc tupdesc, + int mappint_number, const char *filename, int lineno, IdentLine *ident, const char *err_msg); static void fill_ident_view(Tuplestorestate *tuple_store, TupleDesc tupdesc); static ArrayType *gethba_options(HbaLine *hba); /* Number of columns in pg_hba_file_rules view */ -#define NUM_PG_HBA_FILE_RULES_ATTS 9 +#define NUM_PG_HBA_FILE_RULES_ATTS 11 /* * fill_hba_line: build one row of pg_hba_file_rules view, add it to tuplestore @@ -51,7 +53,8 @@ static ArrayType *gethba_options(HbaLine *hba); */ static void fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc, - int lineno, HbaLine *hba, const char *err_msg) + int rule_number, const char *filename, int lineno, HbaLine *hba, + const char *err_msg) { Datum values[NUM_PG_HBA_FILE_RULES_ATTS]; bool nulls[NUM_PG_HBA_FILE_RULES_ATTS]; @@ -70,6 +73,13 @@ fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc, memset(nulls, 0, sizeof(nulls)); index = 0; + /* rule_number */ + if (err_msg) + nulls[index++] = true; + else + values[index++] = Int32GetDatum(rule_number); + /* file_name */ + values[index++] = CStringGetTextDatum(filename); /* line_number */ values[index++] = Int32GetDatum(lineno); @@ -213,7 +223,7 @@ fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc, else { /* no parsing result, so set relevant fields to nulls */ - memset(&nulls[1], true, (NUM_PG_HBA_FILE_RULES_ATTS - 2) * sizeof(bool)); + memset(&nulls[3], true, (NUM_PG_HBA_FILE_RULES_ATTS - 4) * sizeof(bool)); } /* error */ @@ -235,6 +245,7 @@ fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc) FILE *file; List *hba_lines = NIL; ListCell *line; + int rule_number = 0; MemoryContext linecxt; MemoryContext hbacxt; MemoryContext oldcxt; @@ -265,12 +276,15 @@ fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc) TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line); HbaLine *hbaline = NULL; - /* don't parse lines that already have errors */ + /* Only parse lines that don't already have an error */ if (tok_line->err_msg == NULL) + { hbaline = parse_hba_line(tok_line, DEBUG3); + rule_number++; + } - fill_hba_line(tuple_store, tupdesc, tok_line->line_num, - hbaline, tok_line->err_msg); + fill_hba_line(tuple_store, tupdesc, rule_number, tok_line->file_name, + tok_line->line_num, hbaline, tok_line->err_msg); } /* Free tokenizer memory */ @@ -281,7 +295,7 @@ fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc) } /* Number of columns in pg_hba_file_mappings view */ -#define NUM_PG_IDENT_FILE_MAPPINGS_ATTS 5 +#define NUM_PG_IDENT_FILE_MAPPINGS_ATTS 7 /* * fill_ident_line: build one row of pg_ident_file_mappings view, add it to @@ -298,7 +312,8 @@ fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc) */ static void fill_ident_line(Tuplestorestate *tuple_store, TupleDesc tupdesc, - int lineno, IdentLine *ident, const char *err_msg) + int mapping_number, const char *filename, int lineno, + IdentLine *ident, const char *err_msg) { Datum values[NUM_PG_IDENT_FILE_MAPPINGS_ATTS]; bool nulls[NUM_PG_IDENT_FILE_MAPPINGS_ATTS]; @@ -311,6 +326,13 @@ fill_ident_line(Tuplestorestate *tuple_store, TupleDesc tupdesc, memset(nulls, 0, sizeof(nulls)); index = 0; + /* mapping_number */ + if (err_msg) + nulls[index++] = true; + else + values[index++] = Int32GetDatum(mapping_number); + /* file_name */ + values[index++] = CStringGetTextDatum(filename); /* line_number */ values[index++] = Int32GetDatum(lineno); @@ -323,7 +345,7 @@ fill_ident_line(Tuplestorestate *tuple_store, TupleDesc tupdesc, else { /* no parsing result, so set relevant fields to nulls */ - memset(&nulls[1], true, (NUM_PG_IDENT_FILE_MAPPINGS_ATTS - 2) * sizeof(bool)); + memset(&nulls[3], true, (NUM_PG_IDENT_FILE_MAPPINGS_ATTS - 4) * sizeof(bool)); } /* error */ @@ -345,6 +367,7 @@ fill_ident_view(Tuplestorestate *tuple_store, TupleDesc tupdesc) FILE *file; List *ident_lines = NIL; ListCell *line; + int mapping_number = 0; MemoryContext linecxt; MemoryContext identcxt; MemoryContext oldcxt; @@ -375,12 +398,16 @@ fill_ident_view(Tuplestorestate *tuple_store, TupleDesc tupdesc) TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line); IdentLine *identline = NULL; - /* don't parse lines that already have errors */ + /* Only parse lines that don't already have an error */ if (tok_line->err_msg == NULL) + { identline = parse_ident_line(tok_line, DEBUG3); + mapping_number++; + } - fill_ident_line(tuple_store, tupdesc, tok_line->line_num, identline, - tok_line->err_msg); + fill_ident_line(tuple_store, tupdesc, mapping_number, + tok_line->file_name, tok_line->line_num, identline, + tok_line->err_msg); } /* Free tokenizer memory */ diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 04d4edb228..e3baafc3e3 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -6116,16 +6116,16 @@ { oid => '3401', descr => 'show pg_hba.conf rules', proname => 'pg_hba_file_rules', prorows => '1000', proretset => 't', provolatile => 'v', prorettype => 'record', proargtypes => '', - proallargtypes => '{int4,text,_text,_text,text,text,text,_text,text}', - proargmodes => '{o,o,o,o,o,o,o,o,o}', - proargnames => '{line_number,type,database,user_name,address,netmask,auth_method,options,error}', + proallargtypes => '{int4,text,int4,text,_text,_text,text,text,text,_text,text}', + proargmodes => '{o,o,o,o,o,o,o,o,o,o,o}', + proargnames => '{rule_number,file_name,line_number,type,database,user_name,address,netmask,auth_method,options,error}', prosrc => 'pg_hba_file_rules' }, { oid => '9556', descr => 'show pg_ident.conf mappings', proname => 'pg_ident_file_mappings', prorows => '1000', proretset => 't', provolatile => 'v', prorettype => 'record', proargtypes => '', - proallargtypes => '{int4,text,text,text,text}', - proargmodes => '{o,o,o,o,o}', - proargnames => '{line_number,map_name,sys_name,pg_usernamee,error}', + proallargtypes => '{int4,text,int4,text,text,text,text}', + proargmodes => '{o,o,o,o,o,o,o}', + proargnames => '{mapping_number,file_name,line_number,map_name,sys_name,pg_usernamee,error}', prosrc => 'pg_ident_file_mappings' }, { oid => '1371', descr => 'view system lock information', proname => 'pg_lock_status', prorows => '1000', proretset => 't', diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index fce7db248b..551e961585 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -79,6 +79,7 @@ typedef enum ClientCertName typedef struct HbaLine { + char *sourcefile; int linenumber; char *rawline; ConnType conntype; @@ -153,6 +154,7 @@ typedef struct IdentLine typedef struct TokenizedAuthLine { List *fields; /* List of lists of HbaTokens */ + char *file_name; /* File name */ int line_num; /* Line number */ char *raw_line; /* Raw line text */ char *err_msg; /* Error message if any */ diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 62cf0d8674..e6f274cb59 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1337,7 +1337,9 @@ pg_group| SELECT pg_authid.rolname AS groname, WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin); -pg_hba_file_rules| SELECT a.line_number, +pg_hba_file_rules| SELECT a.rule_number, + a.file_name, + a.line_number, a.type, a.database, a.user_name, @@ -1346,13 +1348,15 @@ pg_hba_file_rules| SELECT a.line_number, a.auth_method, a.options, a.error - FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error); -pg_ident_file_mappings| SELECT a.line_number, + FROM pg_hba_file_rules() a(rule_number, file_name, line_number, type, database, user_name, address, netmask, auth_method, options, error); +pg_ident_file_mappings| SELECT a.mapping_number, + a.file_name, + a.line_number, a.map_name, a.sys_name, a.pg_usernamee, a.error - FROM pg_ident_file_mappings() a(line_number, map_name, sys_name, pg_usernamee, error); + FROM pg_ident_file_mappings() a(mapping_number, file_name, line_number, map_name, sys_name, pg_usernamee, error); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, -- 2.33.1