I wrote: > I spent awhile hacking on this, and made a lot of things better, but > I'm still very unhappy about the state of the comments.
I made another pass over this, working on the comments and the docs, and changing the view name to "pg_hba_file_rules". I think this version is committable if people are satisfied with that name. One loose end is what to do about testing. I did not much like the proposed TAP tests. We could just put "select count(*) > 0 from pg_hba_file_rules" into the main regression tests, which would provide some code coverage there, if not very much guarantee that what the view outputs is sane. regards, tom lane
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 086fafc..204b8cf 100644 *** a/doc/src/sgml/catalogs.sgml --- b/doc/src/sgml/catalogs.sgml *************** *** 7809,7814 **** --- 7809,7819 ---- </row> <row> + <entry><link linkend="view-pg-hba-file-rules"><structname>pg_hba_file_rules</structname></link></entry> + <entry>summary of client authentication configuration file contents</entry> + </row> + + <row> <entry><link linkend="view-pg-indexes"><structname>pg_indexes</structname></link></entry> <entry>indexes</entry> </row> *************** *** 8408,8413 **** --- 8413,8526 ---- </sect1> + <sect1 id="view-pg-hba-file-rules"> + <title><structname>pg_hba_file_rules</structname></title> + + <indexterm zone="view-pg-hba-file-rules"> + <primary>pg_hba_file_rules</primary> + </indexterm> + + <para> + The view <structname>pg_hba_file_rules</structname> provides a summary of + the contents of the client authentication configuration + file, <filename>pg_hba.conf</>. A row appears in this view for each + non-empty, non-comment line in the file, with annotations indicating + whether the rule could be applied successfully. + </para> + + <para> + This view can be helpful for checking whether planned changes in the + authentication configuration file will work, or for diagnosing a previous + failure. Note that this view reports on the <emphasis>current</> contents + of the file, not on what was last loaded by the server. + </para> + + <para> + By default, the <structname>pg_hba_file_rules</structname> view can be read + only by superusers. + </para> + + <table> + <title><structname>pg_hba_file_rules</> Columns</title> + + <tgroup cols="3"> + <thead> + <row> + <entry>Name</entry> + <entry>Type</entry> + <entry>Description</entry> + </row> + </thead> + <tbody> + <row> + <entry><structfield>line_number</structfield></entry> + <entry><structfield>integer</structfield></entry> + <entry> + Line number of this rule in <filename>pg_hba.conf</> + </entry> + </row> + <row> + <entry><structfield>type</structfield></entry> + <entry><structfield>text</structfield></entry> + <entry>Type of connection</entry> + </row> + <row> + <entry><structfield>database</structfield></entry> + <entry><structfield>text[]</structfield></entry> + <entry>List of database name(s) to which this rule applies</entry> + </row> + <row> + <entry><structfield>user_name</structfield></entry> + <entry><structfield>text[]</structfield></entry> + <entry>List of user and group name(s) to which this rule applies</entry> + </row> + <row> + <entry><structfield>address</structfield></entry> + <entry><structfield>text</structfield></entry> + <entry> + Host name or IP address, or one + of <literal>all</literal>, <literal>samehost</literal>, + or <literal>samenet</literal>, or null for local connections + </entry> + </row> + <row> + <entry><structfield>netmask</structfield></entry> + <entry><structfield>text</structfield></entry> + <entry>IP address mask, or null if not applicable</entry> + </row> + <row> + <entry><structfield>auth_method</structfield></entry> + <entry><type>text</type></entry> + <entry>Authentication method</entry> + </row> + <row> + <entry><structfield>options</structfield></entry> + <entry><type>text[]</type></entry> + <entry>Options specified for authentication method, if any</entry> + </row> + <row> + <entry><structfield>error</structfield></entry> + <entry><structfield>text</structfield></entry> + <entry> + If not null, an error message indicating why this + line could not be processed + </entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Usually, a row reflecting an incorrect entry will have values for only + the <structfield>line_number</> and <structfield>error</> fields. + </para> + + <para> + See <xref linkend="client-authentication"> for more information about + client authentication configuration. + </para> + </sect1> + <sect1 id="view-pg-indexes"> <title><structname>pg_indexes</structname></title> diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index dda5891..231fc40 100644 *** a/doc/src/sgml/client-auth.sgml --- b/doc/src/sgml/client-auth.sgml *************** hostnossl <replaceable>database</replac *** 597,602 **** --- 597,620 ---- re-read the file. </para> + <note> + <para> + The preceding statement is not true on Microsoft Windows: there, any + changes in the <filename>pg_hba.conf</filename> file are immediately + applied by subsequent new connections. + </para> + </note> + + <para> + The system view + <link linkend="view-pg-hba-file-rules"><structname>pg_hba_file_rules</structname></link> + can be helpful for pre-testing changes to the <filename>pg_hba.conf</> + file, or for diagnosing problems if loading of the file did not have the + desired effects. Rows in the view with + non-null <structfield>error</structfield> fields indicate problems in the + corresponding lines of the file. + </para> + <tip> <para> To connect to a particular database, a user must not only pass the diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 4dfedf8..28be27a 100644 *** a/src/backend/catalog/system_views.sql --- b/src/backend/catalog/system_views.sql *************** CREATE VIEW pg_file_settings AS *** 459,464 **** --- 459,470 ---- REVOKE ALL on pg_file_settings FROM PUBLIC; REVOKE EXECUTE ON FUNCTION pg_show_all_file_settings() FROM PUBLIC; + CREATE VIEW pg_hba_file_rules AS + SELECT * FROM pg_hba_file_rules() AS A; + + REVOKE ALL on pg_hba_file_rules FROM PUBLIC; + REVOKE EXECUTE ON FUNCTION pg_hba_file_rules() FROM PUBLIC; + CREATE VIEW pg_timezone_abbrevs AS SELECT * FROM pg_timezone_abbrevs(); diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index bbe0a88..7a0f1ce 100644 *** a/src/backend/libpq/hba.c --- b/src/backend/libpq/hba.c *************** *** 25,39 **** --- 25,44 ---- #include <arpa/inet.h> #include <unistd.h> + #include "access/htup_details.h" #include "catalog/pg_collation.h" + #include "catalog/pg_type.h" #include "common/ip.h" + #include "funcapi.h" #include "libpq/ifaddr.h" #include "libpq/libpq.h" + #include "miscadmin.h" #include "postmaster/postmaster.h" #include "regex/regex.h" #include "replication/walsender.h" #include "storage/fd.h" #include "utils/acl.h" + #include "utils/builtins.h" #include "utils/guc.h" #include "utils/lsyscache.h" #include "utils/memutils.h" *************** typedef struct HbaToken *** 80,91 **** --- 85,99 ---- * Each item in the "fields" list is a sub-list of HbaTokens. * We don't emit a TokenizedLine for empty or all-comment lines, * so "fields" is never NIL (nor are any of its sub-lists). + * Exception: if an error occurs during tokenization, we might + * have fields == NIL, in which case err_msg != NULL. */ typedef struct TokenizedLine { List *fields; /* List of lists of HbaTokens */ int line_num; /* Line number */ char *raw_line; /* Raw line text */ + char *err_msg; /* Error message if any */ } TokenizedLine; /* *************** static MemoryContext parsed_hba_context *** 106,118 **** static List *parsed_ident_lines = NIL; static MemoryContext parsed_ident_context = NULL; static MemoryContext tokenize_file(const char *filename, FILE *file, ! List **tok_lines); static List *tokenize_inc_file(List *tokens, const char *outer_filename, ! const char *inc_filename); static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, ! int line_num); /* * isblank() exists in the ISO C99 spec, but it's not very portable yet, --- 114,155 ---- static List *parsed_ident_lines = NIL; static MemoryContext parsed_ident_context = NULL; + /* + * The following character array represents the names of the authentication + * methods that are supported by PostgreSQL. + * + * Note: keep this in sync with the UserAuth enum in hba.h. + */ + static const char *const UserAuthName[] = + { + "reject", + "implicit reject", /* Not a user-visible option */ + "trust", + "ident", + "password", + "md5", + "gss", + "sspi", + "pam", + "bsd", + "ldap", + "cert", + "radius", + "peer" + }; + static MemoryContext tokenize_file(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, ! int elevel, char **err_msg); ! static ArrayType *gethba_options(HbaLine *hba); ! static void fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc, ! int lineno, HbaLine *hba, const char *err_msg); ! static void fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc); ! /* * isblank() exists in the ISO C99 spec, but it's not very portable yet, *************** pg_isblank(const char c) *** 126,157 **** /* ! * Grab one token out of the string pointed to by lineptr. * Tokens are strings of non-blank * characters bounded by blank characters, commas, beginning of line, and * end of line. Blank means space or tab. Tokens can be delimited by * double quotes (this allows the inclusion of blanks, but not newlines). * - * The token, if any, is returned at *buf (a buffer of size bufsz). * Also, we set *initial_quote to indicate whether there was quoting before * the first character. (We use that to prevent "@x" from being treated * as a file inclusion request. Note that @"x" should be so treated; * we want to allow that to support embedded spaces in file paths.) * We set *terminating_comma to indicate whether the token is terminated by a ! * comma (which is not returned.) * * If successful: store null-terminated token at *buf and return TRUE. * If no more tokens on line: set *buf = '\0' and return FALSE. ! * ! * Leave file positioned at the character immediately after the token or EOF, ! * whichever comes first. If no more tokens on line, position the file to the ! * beginning of the next line or EOF, whichever comes first. ! * ! * Handle comments. */ static bool ! next_token(char **lineptr, char *buf, int bufsz, bool *initial_quote, ! bool *terminating_comma) { int c; char *start_buf = buf; --- 163,199 ---- /* ! * Grab one token out of the string pointed to by *lineptr. ! * * Tokens are strings of non-blank * characters bounded by blank characters, commas, beginning of line, and * end of line. Blank means space or tab. Tokens can be delimited by * double quotes (this allows the inclusion of blanks, but not newlines). + * Comments (started by an unquoted '#') are skipped. + * + * The token, if any, is returned at *buf (a buffer of size bufsz), and + * *lineptr is advanced past the token. * * Also, we set *initial_quote to indicate whether there was quoting before * the first character. (We use that to prevent "@x" from being treated * as a file inclusion request. Note that @"x" should be so treated; * we want to allow that to support embedded spaces in file paths.) + * * We set *terminating_comma to indicate whether the token is terminated by a ! * comma (which is not returned). ! * ! * In event of an error, log a message at ereport level elevel, and also ! * set *err_msg to a string describing the error. Currently the only ! * possible error is token too long for buf. * * If successful: store null-terminated token at *buf and return TRUE. * If no more tokens on line: set *buf = '\0' and return FALSE. ! * If error: fill buf with truncated or misformatted token and return FALSE. */ static bool ! next_token(char **lineptr, char *buf, int bufsz, ! bool *initial_quote, bool *terminating_comma, ! int elevel, char **err_msg) { int c; char *start_buf = buf; *************** next_token(char **lineptr, char *buf, in *** 197,210 **** if (buf >= end_buf) { *buf = '\0'; ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("authentication file token too long, skipping: \"%s\"", start_buf))); /* Discard remainder of line */ while ((c = (*(*lineptr)++)) != '\0' && c != '\n') ; ! break; } /* we do not pass back the comma in the token */ --- 239,253 ---- if (buf >= end_buf) { *buf = '\0'; ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("authentication file token too long, skipping: \"%s\"", start_buf))); + *err_msg = "authentication file token too long"; /* Discard remainder of line */ while ((c = (*(*lineptr)++)) != '\0' && c != '\n') ; ! return false; } /* we do not pass back the comma in the token */ *************** next_token(char **lineptr, char *buf, in *** 245,257 **** return (saw_quote || buf > start_buf); } static HbaToken * ! make_hba_token(char *token, bool quoted) { HbaToken *hbatoken; int toklen; toklen = strlen(token); hbatoken = (HbaToken *) palloc(sizeof(HbaToken) + toklen + 1); hbatoken->string = (char *) hbatoken + sizeof(HbaToken); hbatoken->quoted = quoted; --- 288,304 ---- return (saw_quote || buf > start_buf); } + /* + * Construct a palloc'd HbaToken struct, copying the given string. + */ static HbaToken * ! make_hba_token(const char *token, bool quoted) { HbaToken *hbatoken; int toklen; toklen = strlen(token); + /* we copy string into same palloc block as the struct */ hbatoken = (HbaToken *) palloc(sizeof(HbaToken) + toklen + 1); hbatoken->string = (char *) hbatoken + sizeof(HbaToken); hbatoken->quoted = quoted; *************** copy_hba_token(HbaToken *in) *** 275,285 **** /* * Tokenize one HBA field from a line, handling file inclusion and comma lists. * ! * The result is a List of HbaToken structs for each individual token, * or NIL if we reached EOL. */ static List * ! next_field_expand(const char *filename, char **lineptr) { char buf[MAX_TOKEN]; bool trailing_comma; --- 322,341 ---- /* * Tokenize one HBA field from a line, handling file inclusion and comma lists. * ! * filename: current file's pathname (needed to resolve relative pathnames) ! * *lineptr: current line pointer, which will be advanced past field ! * ! * In event of an error, log a message at ereport level elevel, and also ! * set *err_msg to a string describing the error. Note that the result ! * may be non-NIL anyway, so *err_msg must be tested to determine whether ! * there was an error. ! * ! * The result is a List of HbaToken structs, one for each token in the field, * or NIL if we reached EOL. */ static List * ! next_field_expand(const char *filename, char **lineptr, ! int elevel, char **err_msg) { char buf[MAX_TOKEN]; bool trailing_comma; *************** next_field_expand(const char *filename, *** 288,302 **** do { ! if (!next_token(lineptr, buf, sizeof(buf), &initial_quote, &trailing_comma)) break; /* Is this referencing a file? */ if (!initial_quote && buf[0] == '@' && buf[1] != '\0') ! tokens = tokenize_inc_file(tokens, filename, buf + 1); else tokens = lappend(tokens, make_hba_token(buf, initial_quote)); ! } while (trailing_comma); return tokens; } --- 344,361 ---- do { ! if (!next_token(lineptr, buf, sizeof(buf), ! &initial_quote, &trailing_comma, ! elevel, err_msg)) break; /* Is this referencing a file? */ if (!initial_quote && buf[0] == '@' && buf[1] != '\0') ! tokens = tokenize_inc_file(tokens, filename, buf + 1, ! elevel, err_msg); else tokens = lappend(tokens, make_hba_token(buf, initial_quote)); ! } while (trailing_comma && (*err_msg == NULL)); return tokens; } *************** next_field_expand(const char *filename, *** 307,319 **** * * Opens and tokenises a file included from another HBA config file with @, * and returns all values found therein as a flat list of HbaTokens. If a ! * @-token is found, recursively expand it. The given token list is used as ! * initial contents of list (so foo,bar,@baz does what you expect). */ static List * tokenize_inc_file(List *tokens, const char *outer_filename, ! const char *inc_filename) { char *inc_fullname; FILE *inc_file; --- 366,386 ---- * * Opens and tokenises a file included from another HBA config file with @, * and returns all values found therein as a flat list of HbaTokens. If a ! * @-token is found, recursively expand it. The newly read tokens are ! * appended to "tokens" (so that foo,bar,@baz does what you expect). ! * All new tokens are allocated in caller's memory context. ! * ! * In event of an error, log a message at ereport level elevel, and also ! * set *err_msg to a string describing the error. Note that the result ! * may be non-NIL anyway, so *err_msg must be tested to determine whether ! * there was an error. */ static List * tokenize_inc_file(List *tokens, const char *outer_filename, ! const char *inc_filename, ! int elevel, ! char **err_msg) { char *inc_fullname; FILE *inc_file; *************** tokenize_inc_file(List *tokens, *** 340,355 **** inc_file = AllocateFile(inc_fullname, "r"); if (inc_file == NULL) { ! ereport(LOG, (errcode_for_file_access(), errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m", inc_filename, inc_fullname))); pfree(inc_fullname); return tokens; } /* There is possible recursion here if the file contains @ */ ! linecxt = tokenize_file(inc_fullname, inc_file, &inc_lines); FreeFile(inc_file); pfree(inc_fullname); --- 407,426 ---- 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_file(inc_fullname, inc_file, &inc_lines, elevel); FreeFile(inc_file); pfree(inc_fullname); *************** tokenize_inc_file(List *tokens, *** 360,365 **** --- 431,443 ---- TokenizedLine *tok_line = (TokenizedLine *) lfirst(inc_line); ListCell *inc_field; + /* If any line has an error, propagate that up to caller */ + if (tok_line->err_msg) + { + *err_msg = pstrdup(tok_line->err_msg); + break; + } + foreach(inc_field, tok_line->fields) { List *inc_tokens = lfirst(inc_field); *************** tokenize_inc_file(List *tokens, *** 383,395 **** * * The output is a list of TokenizedLine structs; see struct definition above. * ! * filename must be the absolute path to the target file. * * Return value is a memory context which contains all memory allocated by * this function (it's a child of caller's context). */ static MemoryContext ! tokenize_file(const char *filename, FILE *file, List **tok_lines) { int line_number = 1; MemoryContext linecxt; --- 461,480 ---- * * The output is a list of TokenizedLine structs; see struct definition above. * ! * 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 TokenizedLine structs containing non-null err_msg fields to the ! * output list. * * Return value is a memory context which contains all memory allocated by * this function (it's a child of caller's context). */ static MemoryContext ! tokenize_file(const char *filename, FILE *file, List **tok_lines, int elevel) { int line_number = 1; MemoryContext linecxt; *************** tokenize_file(const char *filename, FILE *** 407,422 **** char rawline[MAX_LINE]; char *lineptr; List *current_line = NIL; if (!fgets(rawline, sizeof(rawline), file)) ! break; if (strlen(rawline) == MAX_LINE - 1) /* Line too long! */ ! ereport(ERROR, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("authentication file line too long"), errcontext("line %d of configuration file \"%s\"", line_number, filename))); /* Strip trailing linebreak from rawline */ lineptr = rawline + strlen(rawline) - 1; --- 492,523 ---- char rawline[MAX_LINE]; char *lineptr; List *current_line = NIL; + char *err_msg = NULL; if (!fgets(rawline, sizeof(rawline), file)) ! { ! int save_errno = errno; ! ! if (!ferror(file)) ! break; /* normal EOF */ ! /* I/O error! */ ! 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)); ! rawline[0] = '\0'; ! } if (strlen(rawline) == MAX_LINE - 1) + { /* Line too long! */ ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("authentication file line too long"), errcontext("line %d of configuration file \"%s\"", line_number, filename))); + err_msg = "authentication file line too long"; + } /* Strip trailing linebreak from rawline */ lineptr = rawline + strlen(rawline) - 1; *************** tokenize_file(const char *filename, FILE *** 425,442 **** /* Parse fields */ lineptr = rawline; ! while (*lineptr) { List *current_field; ! current_field = next_field_expand(filename, &lineptr); /* 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 TokenizedLine list unless it's boring */ ! if (current_line != NIL) { TokenizedLine *tok_line; --- 526,544 ---- /* Parse fields */ lineptr = rawline; ! 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 TokenizedLine list unless it's boring */ ! if (current_line != NIL || err_msg != NULL) { TokenizedLine *tok_line; *************** tokenize_file(const char *filename, FILE *** 444,449 **** --- 546,552 ---- tok_line->fields = current_line; tok_line->line_num = line_number; tok_line->raw_line = pstrdup(rawline); + tok_line->err_msg = err_msg; *tok_lines = lappend(*tok_lines, tok_line); } *************** check_same_host_or_net(SockAddr *raddr, *** 746,751 **** --- 849,858 ---- /* * Macros used to check and report on invalid configuration options. + * On error: log a message at level elevel, set *err_msg, and exit the function. + * These macros are not as general-purpose as they look, because they know + * what the calling function's error-exit value is. + * * INVALID_AUTH_OPTION = reports when an option is specified for a method where it's * not supported. * REQUIRE_AUTH_OPTION = same as INVALID_AUTH_OPTION, except it also checks if the *************** check_same_host_or_net(SockAddr *raddr, *** 754,797 **** * MANDATORY_AUTH_ARG = check if a required option is set for an authentication method, * reporting error if it's not. */ ! #define INVALID_AUTH_OPTION(optname, validmethods) do {\ ! ereport(LOG, \ (errcode(ERRCODE_CONFIG_FILE_ERROR), \ /* translator: the second %s is a list of auth methods */ \ errmsg("authentication option \"%s\" is only valid for authentication methods %s", \ optname, _(validmethods)), \ errcontext("line %d of configuration file \"%s\"", \ line_num, HbaFileName))); \ return false; \ ! } while (0); ! #define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) do {\ if (hbaline->auth_method != methodval) \ INVALID_AUTH_OPTION(optname, validmethods); \ ! } while (0); ! #define MANDATORY_AUTH_ARG(argvar, argname, authname) do {\ ! if (argvar == NULL) {\ ! ereport(LOG, \ (errcode(ERRCODE_CONFIG_FILE_ERROR), \ errmsg("authentication method \"%s\" requires argument \"%s\" to be set", \ authname, argname), \ errcontext("line %d of configuration file \"%s\"", \ line_num, HbaFileName))); \ return NULL; \ } \ ! } while (0); /* * IDENT_FIELD_ABSENT: ! * Throw an error and exit the function if the given ident field ListCell is * not populated. * * IDENT_MULTI_VALUE: ! * Throw an error and exit the function if the given ident token List has more * than one element. */ ! #define IDENT_FIELD_ABSENT(field) do {\ if (!field) { \ ereport(LOG, \ (errcode(ERRCODE_CONFIG_FILE_ERROR), \ --- 861,916 ---- * MANDATORY_AUTH_ARG = check if a required option is set for an authentication method, * reporting error if it's not. */ ! #define INVALID_AUTH_OPTION(optname, validmethods) \ ! do { \ ! ereport(elevel, \ (errcode(ERRCODE_CONFIG_FILE_ERROR), \ /* translator: the second %s is a list of auth methods */ \ errmsg("authentication option \"%s\" is only valid for authentication methods %s", \ optname, _(validmethods)), \ errcontext("line %d of configuration file \"%s\"", \ line_num, HbaFileName))); \ + *err_msg = psprintf("authentication option \"%s\" is only valid for authentication methods %s", \ + optname, validmethods); \ return false; \ ! } while (0) ! #define REQUIRE_AUTH_OPTION(methodval, optname, validmethods) \ ! do { \ if (hbaline->auth_method != methodval) \ INVALID_AUTH_OPTION(optname, validmethods); \ ! } while (0) ! #define MANDATORY_AUTH_ARG(argvar, argname, authname) \ ! do { \ ! if (argvar == NULL) { \ ! ereport(elevel, \ (errcode(ERRCODE_CONFIG_FILE_ERROR), \ errmsg("authentication method \"%s\" requires argument \"%s\" to be set", \ authname, argname), \ errcontext("line %d of configuration file \"%s\"", \ line_num, HbaFileName))); \ + *err_msg = psprintf("authentication method \"%s\" requires argument \"%s\" to be set", \ + authname, argname); \ return NULL; \ } \ ! } while (0) /* + * Macros for handling pg_ident problems. + * Much as above, but currently the message level is hardwired as LOG + * and there is no provision for an err_msg string. + * * IDENT_FIELD_ABSENT: ! * Log a message and exit the function if the given ident field ListCell is * not populated. * * IDENT_MULTI_VALUE: ! * Log a message and exit the function if the given ident token List has more * than one element. */ ! #define IDENT_FIELD_ABSENT(field) \ ! do { \ if (!field) { \ ereport(LOG, \ (errcode(ERRCODE_CONFIG_FILE_ERROR), \ *************** check_same_host_or_net(SockAddr *raddr, *** 799,807 **** IdentFileName, line_num))); \ return NULL; \ } \ ! } while (0); ! #define IDENT_MULTI_VALUE(tokens) do {\ if (tokens->length > 1) { \ ereport(LOG, \ (errcode(ERRCODE_CONFIG_FILE_ERROR), \ --- 918,927 ---- IdentFileName, line_num))); \ return NULL; \ } \ ! } while (0) ! #define IDENT_MULTI_VALUE(tokens) \ ! do { \ if (tokens->length > 1) { \ ereport(LOG, \ (errcode(ERRCODE_CONFIG_FILE_ERROR), \ *************** check_same_host_or_net(SockAddr *raddr, *** 810,832 **** line_num, IdentFileName))); \ return NULL; \ } \ ! } while (0); /* * Parse one tokenised line from the hba config file and store the result in a * HbaLine structure. * ! * Return NULL if parsing fails. * * 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 HbaLine * ! parse_hba_line(TokenizedLine *tok_line) { int line_num = tok_line->line_num; char *str; struct addrinfo *gai_result; struct addrinfo hints; --- 930,955 ---- line_num, IdentFileName))); \ return NULL; \ } \ ! } while (0) /* * Parse one tokenised line from the hba config file and store the result in a * HbaLine structure. * ! * If parsing fails, log a message at ereport level elevel, store an error ! * string in tok_line->err_msg, and return NULL. (Some non-error conditions ! * can also result in such messages.) * * 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 HbaLine * ! parse_hba_line(TokenizedLine *tok_line, int elevel) { int line_num = tok_line->line_num; + char **err_msg = &tok_line->err_msg; char *str; struct addrinfo *gai_result; struct addrinfo hints; *************** parse_hba_line(TokenizedLine *tok_line) *** 849,860 **** tokens = lfirst(field); if (tokens->length > 1) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("multiple values specified for connection type"), errhint("Specify exactly one connection type per line."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } token = linitial(tokens); --- 972,984 ---- tokens = lfirst(field); if (tokens->length > 1) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("multiple values specified for connection type"), errhint("Specify exactly one connection type per line."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "multiple values specified for connection type"; return NULL; } token = linitial(tokens); *************** parse_hba_line(TokenizedLine *tok_line) *** 863,873 **** #ifdef HAVE_UNIX_SOCKETS parsedline->conntype = ctLocal; #else ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("local connections are not supported by this build"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; #endif } --- 987,998 ---- #ifdef HAVE_UNIX_SOCKETS parsedline->conntype = ctLocal; #else ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("local connections are not supported by this build"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "local connections are not supported by this build"; return NULL; #endif } *************** parse_hba_line(TokenizedLine *tok_line) *** 882,900 **** /* Log a warning if SSL support is not active */ #ifdef USE_SSL if (!EnableSSL) ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("hostssl record cannot match because SSL is disabled"), errhint("Set ssl = on in postgresql.conf."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); #else ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("hostssl record cannot match because SSL is not supported by this build"), errhint("Compile with --with-openssl to use SSL connections."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); #endif } else if (token->string[4] == 'n') /* "hostnossl" */ --- 1007,1029 ---- /* Log a warning if SSL support is not active */ #ifdef USE_SSL if (!EnableSSL) ! { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("hostssl record cannot match because SSL is disabled"), errhint("Set ssl = on in postgresql.conf."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "hostssl record cannot match because SSL is disabled"; + } #else ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("hostssl record cannot match because SSL is not supported by this build"), errhint("Compile with --with-openssl to use SSL connections."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "hostssl record cannot match because SSL is not supported by this build"; #endif } else if (token->string[4] == 'n') /* "hostnossl" */ *************** parse_hba_line(TokenizedLine *tok_line) *** 909,920 **** } /* record type */ else { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("invalid connection type \"%s\"", token->string), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } --- 1038,1050 ---- } /* record type */ else { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("invalid connection type \"%s\"", token->string), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = psprintf("invalid connection type \"%s\"", token->string); return NULL; } *************** parse_hba_line(TokenizedLine *tok_line) *** 922,932 **** field = lnext(field); if (!field) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("end-of-line before database specification"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } parsedline->databases = NIL; --- 1052,1063 ---- field = lnext(field); if (!field) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("end-of-line before database specification"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "end-of-line before database specification"; return NULL; } parsedline->databases = NIL; *************** parse_hba_line(TokenizedLine *tok_line) *** 941,951 **** field = lnext(field); if (!field) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("end-of-line before role specification"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } parsedline->roles = NIL; --- 1072,1083 ---- field = lnext(field); if (!field) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("end-of-line before role specification"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "end-of-line before role specification"; return NULL; } parsedline->roles = NIL; *************** parse_hba_line(TokenizedLine *tok_line) *** 962,983 **** field = lnext(field); if (!field) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("end-of-line before IP address specification"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } tokens = lfirst(field); if (tokens->length > 1) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("multiple values specified for host address"), errhint("Specify one address range per line."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } token = linitial(tokens); --- 1094,1117 ---- field = lnext(field); if (!field) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("end-of-line before IP address specification"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "end-of-line before IP address specification"; return NULL; } tokens = lfirst(field); if (tokens->length > 1) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("multiple values specified for host address"), errhint("Specify one address range per line."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "multiple values specified for host address"; return NULL; } token = linitial(tokens); *************** parse_hba_line(TokenizedLine *tok_line) *** 1027,1038 **** parsedline->hostname = str; else { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("invalid IP address \"%s\": %s", str, gai_strerror(ret)), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); if (gai_result) pg_freeaddrinfo_all(hints.ai_family, gai_result); return NULL; --- 1161,1174 ---- parsedline->hostname = str; else { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("invalid IP address \"%s\": %s", str, gai_strerror(ret)), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = psprintf("invalid IP address \"%s\": %s", + str, gai_strerror(ret)); if (gai_result) pg_freeaddrinfo_all(hints.ai_family, gai_result); return NULL; *************** parse_hba_line(TokenizedLine *tok_line) *** 1045,1068 **** { if (parsedline->hostname) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("specifying both host name and CIDR mask is invalid: \"%s\"", token->string), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1, parsedline->addr.ss_family) < 0) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("invalid CIDR mask in address \"%s\"", token->string), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } pfree(str); --- 1181,1208 ---- { if (parsedline->hostname) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("specifying both host name and CIDR mask is invalid: \"%s\"", token->string), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = psprintf("specifying both host name and CIDR mask is invalid: \"%s\"", + token->string); return NULL; } if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1, parsedline->addr.ss_family) < 0) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("invalid CIDR mask in address \"%s\"", token->string), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = psprintf("invalid CIDR mask in address \"%s\"", + token->string); return NULL; } pfree(str); *************** parse_hba_line(TokenizedLine *tok_line) *** 1074,1095 **** field = lnext(field); if (!field) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("end-of-line before netmask specification"), errhint("Specify an address range in CIDR notation, or provide a separate netmask."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } tokens = lfirst(field); if (tokens->length > 1) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("multiple values specified for netmask"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } token = linitial(tokens); --- 1214,1237 ---- field = lnext(field); if (!field) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("end-of-line before netmask specification"), errhint("Specify an address range in CIDR notation, or provide a separate netmask."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "end-of-line before netmask specification"; return NULL; } tokens = lfirst(field); if (tokens->length > 1) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("multiple values specified for netmask"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "multiple values specified for netmask"; return NULL; } token = linitial(tokens); *************** parse_hba_line(TokenizedLine *tok_line) *** 1098,1109 **** &hints, &gai_result); if (ret || !gai_result) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("invalid IP mask \"%s\": %s", token->string, gai_strerror(ret)), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); if (gai_result) pg_freeaddrinfo_all(hints.ai_family, gai_result); return NULL; --- 1240,1253 ---- &hints, &gai_result); if (ret || !gai_result) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("invalid IP mask \"%s\": %s", token->string, gai_strerror(ret)), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = psprintf("invalid IP mask \"%s\": %s", + token->string, gai_strerror(ret)); if (gai_result) pg_freeaddrinfo_all(hints.ai_family, gai_result); return NULL; *************** parse_hba_line(TokenizedLine *tok_line) *** 1115,1125 **** if (parsedline->addr.ss_family != parsedline->mask.ss_family) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("IP address and mask do not match"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } } --- 1259,1270 ---- if (parsedline->addr.ss_family != parsedline->mask.ss_family) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("IP address and mask do not match"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "IP address and mask do not match"; return NULL; } } *************** parse_hba_line(TokenizedLine *tok_line) *** 1130,1151 **** field = lnext(field); if (!field) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("end-of-line before authentication method"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } tokens = lfirst(field); if (tokens->length > 1) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("multiple values specified for authentication type"), errhint("Specify exactly one authentication type per line."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } token = linitial(tokens); --- 1275,1298 ---- field = lnext(field); if (!field) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("end-of-line before authentication method"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "end-of-line before authentication method"; return NULL; } tokens = lfirst(field); if (tokens->length > 1) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("multiple values specified for authentication type"), errhint("Specify exactly one authentication type per line."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "multiple values specified for authentication type"; return NULL; } token = linitial(tokens); *************** parse_hba_line(TokenizedLine *tok_line) *** 1177,1187 **** { if (Db_user_namespace) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } parsedline->auth_method = uaMD5; --- 1324,1335 ---- { if (Db_user_namespace) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "MD5 authentication is not supported when \"db_user_namespace\" is enabled"; return NULL; } parsedline->auth_method = uaMD5; *************** parse_hba_line(TokenizedLine *tok_line) *** 1214,1236 **** parsedline->auth_method = uaRADIUS; else { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("invalid authentication method \"%s\"", token->string), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } if (unsupauth) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("invalid authentication method \"%s\": not supported by this build", token->string), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } --- 1362,1388 ---- parsedline->auth_method = uaRADIUS; else { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("invalid authentication method \"%s\"", token->string), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = psprintf("invalid authentication method \"%s\"", + token->string); return NULL; } if (unsupauth) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("invalid authentication method \"%s\": not supported by this build", token->string), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = psprintf("invalid authentication method \"%s\": not supported by this build", + token->string); return NULL; } *************** parse_hba_line(TokenizedLine *tok_line) *** 1246,1267 **** if (parsedline->conntype == ctLocal && parsedline->auth_method == uaGSS) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("gssapi authentication is not supported on local sockets"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } if (parsedline->conntype != ctLocal && parsedline->auth_method == uaPeer) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("peer authentication is only supported on local sockets"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } --- 1398,1421 ---- if (parsedline->conntype == ctLocal && parsedline->auth_method == uaGSS) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("gssapi authentication is not supported on local sockets"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "gssapi authentication is not supported on local sockets"; return NULL; } if (parsedline->conntype != ctLocal && parsedline->auth_method == uaPeer) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("peer authentication is only supported on local sockets"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "peer authentication is only supported on local sockets"; return NULL; } *************** parse_hba_line(TokenizedLine *tok_line) *** 1274,1284 **** if (parsedline->conntype != ctHostSSL && parsedline->auth_method == uaCert) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("cert authentication is only supported on hostssl connections"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } --- 1428,1439 ---- if (parsedline->conntype != ctHostSSL && parsedline->auth_method == uaCert) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("cert authentication is only supported on hostssl connections"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "cert authentication is only supported on hostssl connections"; return NULL; } *************** parse_hba_line(TokenizedLine *tok_line) *** 1323,1338 **** /* * Got something that's not a name=value pair. */ ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("authentication option not in name=value format: %s", token->string), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } *val++ = '\0'; /* str now holds "name", val holds "value" */ ! if (!parse_hba_auth_opt(str, val, parsedline, line_num)) /* parse_hba_auth_opt already logged the error message */ return NULL; pfree(str); --- 1478,1495 ---- /* * Got something that's not a name=value pair. */ ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("authentication option not in name=value format: %s", token->string), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = psprintf("authentication option not in name=value format: %s", + token->string); return NULL; } *val++ = '\0'; /* str now holds "name", val holds "value" */ ! if (!parse_hba_auth_opt(str, val, parsedline, elevel, err_msg)) /* parse_hba_auth_opt already logged the error message */ return NULL; pfree(str); *************** parse_hba_line(TokenizedLine *tok_line) *** 1360,1380 **** parsedline->ldapbindpasswd || parsedline->ldapsearchattribute) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, ldapsearchattribute, or ldapurl together with ldapprefix"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } } else if (!parsedline->ldapbasedn) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; } } --- 1517,1539 ---- parsedline->ldapbindpasswd || parsedline->ldapsearchattribute) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, ldapsearchattribute, or ldapurl together with ldapprefix"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, ldapsearchattribute, or ldapurl together with ldapprefix"; return NULL; } } else if (!parsedline->ldapbasedn) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"; return NULL; } } *************** parse_hba_line(TokenizedLine *tok_line) *** 1399,1409 **** /* * Parse one name-value pair as an authentication option into the given * HbaLine. Return true if we successfully parse the option, false if we ! * encounter an error. */ static bool ! parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num) { #ifdef USE_LDAP hbaline->ldapscope = LDAP_SCOPE_SUBTREE; #endif --- 1558,1572 ---- /* * Parse one name-value pair as an authentication option into the given * HbaLine. Return true if we successfully parse the option, false if we ! * encounter an error. In the event of an error, also log a message at ! * ereport level elevel, and store a message string into *err_msg. */ static bool ! parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, ! int elevel, char **err_msg) { + int line_num = hbaline->linenumber; + #ifdef USE_LDAP hbaline->ldapscope = LDAP_SCOPE_SUBTREE; #endif *************** parse_hba_auth_opt(char *name, char *val *** 1422,1432 **** { if (hbaline->conntype != ctHostSSL) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("clientcert can only be configured for \"hostssl\" rows"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return false; } if (strcmp(val, "1") == 0) --- 1585,1596 ---- { if (hbaline->conntype != ctHostSSL) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("clientcert can only be configured for \"hostssl\" rows"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "clientcert can only be configured for \"hostssl\" rows"; return false; } if (strcmp(val, "1") == 0) *************** parse_hba_auth_opt(char *name, char *val *** 1437,1447 **** { if (hbaline->auth_method == uaCert) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("clientcert can not be set to 0 when using \"cert\" authentication"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return false; } hbaline->clientcert = false; --- 1601,1612 ---- { if (hbaline->auth_method == uaCert) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("clientcert can not be set to 0 when using \"cert\" authentication"), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = "clientcert can not be set to 0 when using \"cert\" authentication"; return false; } hbaline->clientcert = false; *************** parse_hba_auth_opt(char *name, char *val *** 1473,1489 **** rc = ldap_url_parse(val, &urldata); if (rc != LDAP_SUCCESS) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("could not parse LDAP URL \"%s\": %s", val, ldap_err2string(rc)))); return false; } if (strcmp(urldata->lud_scheme, "ldap") != 0) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("unsupported LDAP URL scheme: %s", urldata->lud_scheme))); ldap_free_urldesc(urldata); return false; } --- 1638,1658 ---- rc = ldap_url_parse(val, &urldata); if (rc != LDAP_SUCCESS) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("could not parse LDAP URL \"%s\": %s", val, ldap_err2string(rc)))); + *err_msg = psprintf("could not parse LDAP URL \"%s\": %s", + val, ldap_err2string(rc)); return false; } if (strcmp(urldata->lud_scheme, "ldap") != 0) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("unsupported LDAP URL scheme: %s", urldata->lud_scheme))); + *err_msg = psprintf("unsupported LDAP URL scheme: %s", + urldata->lud_scheme); ldap_free_urldesc(urldata); return false; } *************** parse_hba_auth_opt(char *name, char *val *** 1497,1513 **** hbaline->ldapscope = urldata->lud_scope; if (urldata->lud_filter) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("filters not supported in LDAP URLs"))); ldap_free_urldesc(urldata); return false; } ldap_free_urldesc(urldata); #else /* not OpenLDAP */ ! ereport(LOG, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("LDAP URLs not supported on this platform"))); #endif /* not OpenLDAP */ } else if (strcmp(name, "ldaptls") == 0) --- 1666,1684 ---- hbaline->ldapscope = urldata->lud_scope; if (urldata->lud_filter) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("filters not supported in LDAP URLs"))); + *err_msg = "filters not supported in LDAP URLs"; ldap_free_urldesc(urldata); return false; } ldap_free_urldesc(urldata); #else /* not OpenLDAP */ ! ereport(elevel, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("LDAP URLs not supported on this platform"))); + *err_msg = "LDAP URLs not supported on this platform"; #endif /* not OpenLDAP */ } else if (strcmp(name, "ldaptls") == 0) *************** parse_hba_auth_opt(char *name, char *val *** 1529,1539 **** hbaline->ldapport = atoi(val); if (hbaline->ldapport == 0) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("invalid LDAP port number: \"%s\"", val), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return false; } } --- 1700,1711 ---- hbaline->ldapport = atoi(val); if (hbaline->ldapport == 0) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("invalid LDAP port number: \"%s\"", val), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = psprintf("invalid LDAP port number: \"%s\"", val); return false; } } *************** parse_hba_auth_opt(char *name, char *val *** 1617,1628 **** ret = pg_getaddrinfo_all(val, NULL, &hints, &gai_result); if (ret || !gai_result) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("could not translate RADIUS server name \"%s\" to address: %s", val, gai_strerror(ret)), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); if (gai_result) pg_freeaddrinfo_all(hints.ai_family, gai_result); return false; --- 1789,1802 ---- ret = pg_getaddrinfo_all(val, NULL, &hints, &gai_result); if (ret || !gai_result) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("could not translate RADIUS server name \"%s\" to address: %s", val, gai_strerror(ret)), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = psprintf("could not translate RADIUS server name \"%s\" to address: %s", + val, gai_strerror(ret)); if (gai_result) pg_freeaddrinfo_all(hints.ai_family, gai_result); return false; *************** parse_hba_auth_opt(char *name, char *val *** 1636,1646 **** hbaline->radiusport = atoi(val); if (hbaline->radiusport == 0) { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("invalid RADIUS port number: \"%s\"", val), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return false; } } --- 1810,1821 ---- hbaline->radiusport = atoi(val); if (hbaline->radiusport == 0) { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("invalid RADIUS port number: \"%s\"", val), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = psprintf("invalid RADIUS port number: \"%s\"", val); return false; } } *************** parse_hba_auth_opt(char *name, char *val *** 1656,1667 **** } else { ! ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("unrecognized authentication option name: \"%s\"", name), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return false; } return true; --- 1831,1844 ---- } else { ! ereport(elevel, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("unrecognized authentication option name: \"%s\"", name), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + *err_msg = psprintf("unrecognized authentication option name: \"%s\"", + name); return false; } return true; *************** load_hba(void) *** 1794,1800 **** return false; } ! linecxt = tokenize_file(HbaFileName, file, &hba_lines); FreeFile(file); /* Now parse all the lines */ --- 1971,1977 ---- return false; } ! linecxt = tokenize_file(HbaFileName, file, &hba_lines, LOG); FreeFile(file); /* Now parse all the lines */ *************** load_hba(void) *** 1808,1828 **** TokenizedLine *tok_line = (TokenizedLine *) lfirst(line); HbaLine *newline; ! if ((newline = parse_hba_line(tok_line)) == NULL) { ! /* ! * Parse error in the file, so indicate there's a problem. NB: a ! * problem in a line will free the memory for all previous lines ! * as well! ! */ ! MemoryContextReset(hbacxt); ! 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; } --- 1985,2006 ---- TokenizedLine *tok_line = (TokenizedLine *) lfirst(line); HbaLine *newline; ! /* don't parse lines that already have errors */ ! if (tok_line->err_msg != NULL) { ! ok = false; ! continue; ! } ! ! if ((newline = parse_hba_line(tok_line, LOG)) == NULL) ! { ! /* Parse error; remember there's trouble */ ok = false; /* * Keep parsing the rest of the file so we can report errors on ! * more than the first line. Error has already been logged, no ! * need for more chatter here. */ continue; } *************** load_hba(void) *** 1865,1874 **** } /* * Parse one tokenised line from the ident config file and store the result in * an IdentLine structure. * ! * Return NULL if parsing fails. * * If ident_user is a regular expression (ie. begins with a slash), it is * compiled and stored in IdentLine structure. --- 2043,2454 ---- } /* + * This macro specifies the maximum number of authentication options + * that are possible with any given authentication method that is supported. + * Currently LDAP supports 10, so the macro value is well above the most any + * method needs. + */ + #define MAX_HBA_OPTIONS 12 + + /* + * Create a text array listing the options specified in the HBA line. + * Return NULL if no options are specified. + */ + static ArrayType * + gethba_options(HbaLine *hba) + { + int noptions; + Datum options[MAX_HBA_OPTIONS]; + + noptions = 0; + + if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI) + { + if (hba->include_realm) + options[noptions++] = + CStringGetTextDatum("include_realm=true"); + + if (hba->krb_realm) + options[noptions++] = + CStringGetTextDatum(psprintf("krb_realm=%s", hba->krb_realm)); + } + + if (hba->usermap) + options[noptions++] = + CStringGetTextDatum(psprintf("map=%s", hba->usermap)); + + if (hba->clientcert) + options[noptions++] = + CStringGetTextDatum("clientcert=true"); + + if (hba->pamservice) + options[noptions++] = + CStringGetTextDatum(psprintf("pamservice=%s", hba->pamservice)); + + if (hba->auth_method == uaLDAP) + { + if (hba->ldapserver) + options[noptions++] = + CStringGetTextDatum(psprintf("ldapserver=%s", hba->ldapserver)); + + if (hba->ldapport) + options[noptions++] = + CStringGetTextDatum(psprintf("ldapport=%d", hba->ldapport)); + + if (hba->ldaptls) + options[noptions++] = + CStringGetTextDatum("ldaptls=true"); + + if (hba->ldapprefix) + options[noptions++] = + CStringGetTextDatum(psprintf("ldapprefix=%s", hba->ldapprefix)); + + if (hba->ldapsuffix) + options[noptions++] = + CStringGetTextDatum(psprintf("ldapsuffix=%s", hba->ldapsuffix)); + + if (hba->ldapbasedn) + options[noptions++] = + CStringGetTextDatum(psprintf("ldapbasedn=%s", hba->ldapbasedn)); + + if (hba->ldapbinddn) + options[noptions++] = + CStringGetTextDatum(psprintf("ldapbinddn=%s", hba->ldapbinddn)); + + if (hba->ldapbindpasswd) + options[noptions++] = + CStringGetTextDatum(psprintf("ldapbindpasswd=%s", + hba->ldapbindpasswd)); + + if (hba->ldapsearchattribute) + options[noptions++] = + CStringGetTextDatum(psprintf("ldapsearchattribute=%s", + hba->ldapsearchattribute)); + + if (hba->ldapscope) + options[noptions++] = + CStringGetTextDatum(psprintf("ldapscope=%d", hba->ldapscope)); + } + + if (hba->auth_method == uaRADIUS) + { + if (hba->radiusserver) + options[noptions++] = + CStringGetTextDatum(psprintf("radiusserver=%s", hba->radiusserver)); + + if (hba->radiussecret) + options[noptions++] = + CStringGetTextDatum(psprintf("radiussecret=%s", hba->radiussecret)); + + if (hba->radiusidentifier) + options[noptions++] = + CStringGetTextDatum(psprintf("radiusidentifier=%s", hba->radiusidentifier)); + + if (hba->radiusport) + options[noptions++] = + CStringGetTextDatum(psprintf("radiusport=%d", hba->radiusport)); + } + + Assert(noptions <= MAX_HBA_OPTIONS); + + if (noptions > 0) + return construct_array(options, noptions, TEXTOID, -1, false, 'i'); + else + return NULL; + } + + /* Number of columns in pg_hba_file_rules view */ + #define NUM_PG_HBA_FILE_RULES_ATTS 9 + + /* + * fill_hba_line: build one row of pg_hba_file_rules view, add it to tuplestore + * + * tuple_store: where to store data + * tupdesc: tuple descriptor for the view + * lineno: pg_hba.conf line number (must always be valid) + * hba: parsed line data (can be NULL, in which case err_msg should be set) + * err_msg: error message (NULL if none) + * + * Note: leaks memory, but we don't care since this is run in a short-lived + * memory context. + */ + static void + fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc, + int lineno, HbaLine *hba, const char *err_msg) + { + Datum values[NUM_PG_HBA_FILE_RULES_ATTS]; + bool nulls[NUM_PG_HBA_FILE_RULES_ATTS]; + char buffer[NI_MAXHOST]; + HeapTuple tuple; + int index; + ListCell *lc; + const char *typestr; + const char *addrstr; + const char *maskstr; + ArrayType *options; + + Assert(tupdesc->natts == NUM_PG_HBA_FILE_RULES_ATTS); + + memset(values, 0, sizeof(values)); + memset(nulls, 0, sizeof(nulls)); + index = 0; + + /* line_number */ + values[index++] = Int32GetDatum(lineno); + + if (hba != NULL) + { + /* type */ + /* Avoid a default: case so compiler will warn about missing cases */ + typestr = NULL; + switch (hba->conntype) + { + case ctLocal: + typestr = "local"; + break; + case ctHost: + typestr = "host"; + break; + case ctHostSSL: + typestr = "hostssl"; + break; + case ctHostNoSSL: + typestr = "hostnossl"; + break; + } + if (typestr) + values[index++] = CStringGetTextDatum(typestr); + else + nulls[index++] = true; + + /* database */ + if (hba->databases) + { + /* + * Flatten HbaToken list to string list. It might seem that we + * should re-quote any quoted tokens, but that has been rejected + * on the grounds that it makes it harder to compare the array + * elements to other system catalogs. That makes entries like + * "all" or "samerole" formally ambiguous ... but users who name + * databases/roles that way are inflicting their own pain. + */ + List *names = NIL; + + foreach(lc, hba->databases) + { + HbaToken *tok = lfirst(lc); + + names = lappend(names, tok->string); + } + values[index++] = PointerGetDatum(strlist_to_textarray(names)); + } + else + nulls[index++] = true; + + /* user */ + if (hba->roles) + { + /* Flatten HbaToken list to string list; see comment above */ + List *roles = NIL; + + foreach(lc, hba->roles) + { + HbaToken *tok = lfirst(lc); + + roles = lappend(roles, tok->string); + } + values[index++] = PointerGetDatum(strlist_to_textarray(roles)); + } + else + nulls[index++] = true; + + /* address and netmask */ + /* Avoid a default: case so compiler will warn about missing cases */ + addrstr = maskstr = NULL; + switch (hba->ip_cmp_method) + { + case ipCmpMask: + if (hba->hostname) + { + addrstr = hba->hostname; + } + else + { + if (pg_getnameinfo_all(&hba->addr, sizeof(hba->addr), + buffer, sizeof(buffer), + NULL, 0, + NI_NUMERICHOST) == 0) + { + clean_ipv6_addr(hba->addr.ss_family, buffer); + addrstr = pstrdup(buffer); + } + if (pg_getnameinfo_all(&hba->mask, sizeof(hba->mask), + buffer, sizeof(buffer), + NULL, 0, + NI_NUMERICHOST) == 0) + { + clean_ipv6_addr(hba->mask.ss_family, buffer); + maskstr = pstrdup(buffer); + } + } + break; + case ipCmpAll: + addrstr = "all"; + break; + case ipCmpSameHost: + addrstr = "samehost"; + break; + case ipCmpSameNet: + addrstr = "samenet"; + break; + } + if (addrstr) + values[index++] = CStringGetTextDatum(addrstr); + else + nulls[index++] = true; + if (maskstr) + values[index++] = CStringGetTextDatum(maskstr); + else + nulls[index++] = true; + + /* auth_method */ + values[index++] = CStringGetTextDatum(UserAuthName[hba->auth_method]); + + /* options */ + options = gethba_options(hba); + if (options) + values[index++] = PointerGetDatum(options); + else + nulls[index++] = true; + } + else + { + /* no parsing result, so set relevant fields to nulls */ + memset(&nulls[1], true, (NUM_PG_HBA_FILE_RULES_ATTS - 2) * sizeof(bool)); + } + + /* error */ + if (err_msg) + values[NUM_PG_HBA_FILE_RULES_ATTS - 1] = CStringGetTextDatum(err_msg); + else + nulls[NUM_PG_HBA_FILE_RULES_ATTS - 1] = true; + + tuple = heap_form_tuple(tupdesc, values, nulls); + tuplestore_puttuple(tuple_store, tuple); + } + + /* + * Read the pg_hba.conf file and fill the tuplestore with view records. + */ + static void + fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc) + { + FILE *file; + List *hba_lines = NIL; + ListCell *line; + MemoryContext linecxt; + MemoryContext hbacxt; + MemoryContext oldcxt; + + /* + * In the unlikely event that we can't open pg_hba.conf, we throw an + * error, rather than trying to report it via some sort of view entry. + * (Most other error conditions should result in a message in a view + * entry.) + */ + file = AllocateFile(HbaFileName, "r"); + if (file == NULL) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not open configuration file \"%s\": %m", + HbaFileName))); + + linecxt = tokenize_file(HbaFileName, file, &hba_lines, DEBUG3); + FreeFile(file); + + /* Now parse all the lines */ + hbacxt = AllocSetContextCreate(CurrentMemoryContext, + "hba parser context", + ALLOCSET_SMALL_SIZES); + oldcxt = MemoryContextSwitchTo(hbacxt); + foreach(line, hba_lines) + { + TokenizedLine *tok_line = (TokenizedLine *) lfirst(line); + HbaLine *hbaline = NULL; + + /* don't parse lines that already have errors */ + if (tok_line->err_msg == NULL) + hbaline = parse_hba_line(tok_line, DEBUG3); + + fill_hba_line(tuple_store, tupdesc, tok_line->line_num, + hbaline, tok_line->err_msg); + } + + /* Free tokenizer memory */ + MemoryContextDelete(linecxt); + /* Free parse_hba_line memory */ + MemoryContextSwitchTo(oldcxt); + MemoryContextDelete(hbacxt); + } + + /* + * SQL-accessible SRF to return all the entries in the pg_hba.conf file. + */ + Datum + pg_hba_file_rules(PG_FUNCTION_ARGS) + { + Tuplestorestate *tuple_store; + TupleDesc tupdesc; + MemoryContext old_cxt; + ReturnSetInfo *rsi; + + /* + * We must use the Materialize mode to be safe against HBA file changes + * while the cursor is open. It's also more efficient than having to look + * up our current position in the parsed list every time. + */ + rsi = (ReturnSetInfo *) fcinfo->resultinfo; + + /* Check to see if caller supports us returning a tuplestore */ + if (rsi == NULL || !IsA(rsi, ReturnSetInfo)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + if (!(rsi->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not " \ + "allowed in this context"))); + + rsi->returnMode = SFRM_Materialize; + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + /* Build tuplestore to hold the result rows */ + old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory); + + tuple_store = + tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random, + false, work_mem); + rsi->setDesc = tupdesc; + rsi->setResult = tuple_store; + + MemoryContextSwitchTo(old_cxt); + + /* Fill the tuplestore */ + fill_hba_view(tuple_store, tupdesc); + + PG_RETURN_NULL(); + } + + + /* * Parse one tokenised line from the ident config file and store the result in * an IdentLine structure. * ! * If parsing fails, log a message and return NULL. * * If ident_user is a regular expression (ie. begins with a slash), it is * compiled and stored in IdentLine structure. *************** load_ident(void) *** 2170,2176 **** return false; } ! linecxt = tokenize_file(IdentFileName, file, &ident_lines); FreeFile(file); /* Now parse all the lines */ --- 2750,2756 ---- return false; } ! linecxt = tokenize_file(IdentFileName, file, &ident_lines, LOG); FreeFile(file); /* Now parse all the lines */ *************** load_ident(void) *** 2183,2208 **** { TokenizedLine *tok_line = (TokenizedLine *) lfirst(line_cell); if ((newline = parse_ident_line(tok_line)) == 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; } --- 2763,2784 ---- { TokenizedLine *tok_line = (TokenizedLine *) lfirst(line_cell); + /* don't parse lines that already have errors */ + if (tok_line->err_msg != NULL) + { + ok = false; + continue; + } + if ((newline = parse_ident_line(tok_line)) == NULL) { ! /* Parse error; remember there's trouble */ ok = false; /* * Keep parsing the rest of the file so we can report errors on ! * more than the first line. Error has already been logged, no ! * need for more chatter here. */ continue; } *************** load_ident(void) *** 2216,2222 **** if (!ok) { ! /* File contained one or more errors, so bail out */ foreach(parsed_line_cell, new_parsed_lines) { newline = (IdentLine *) lfirst(parsed_line_cell); --- 2792,2802 ---- if (!ok) { ! /* ! * File contained one or more errors, so bail out, first being careful ! * to clean up whatever we allocated. Most stuff will go away via ! * MemoryContextDelete, but we have to clean up regexes explicitly. ! */ foreach(parsed_line_cell, new_parsed_lines) { newline = (IdentLine *) lfirst(parsed_line_cell); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 31c828a..05652e8 100644 *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** DATA(insert OID = 2084 ( pg_show_all_se *** 3076,3081 **** --- 3076,3083 ---- DESCR("SHOW ALL as a function"); DATA(insert OID = 3329 ( pg_show_all_file_settings PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{25,23,23,25,25,16,25}" "{o,o,o,o,o,o,o}" "{sourcefile,sourceline,seqno,name,setting,applied,error}" _null_ _null_ show_all_file_settings _null_ _null_ _null_ )); DESCR("show config file settings"); + DATA(insert OID = 3401 ( pg_hba_file_rules PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{23,25,1009,1009,25,25,25,1009,25}" "{o,o,o,o,o,o,o,o,o}" "{line_number,type,database,user_name,address,netmask,auth_method,options,error}" _null_ _null_ pg_hba_file_rules _null_ _null_ _null_ )); + DESCR("show pg_hba.conf rules"); DATA(insert OID = 1371 ( pg_lock_status PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{25,26,26,23,21,25,28,26,26,21,25,23,25,16,16}" "{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{locktype,database,relation,page,tuple,virtualxid,transactionid,classid,objid,objsubid,virtualtransaction,pid,mode,granted,fastpath}" _null_ _null_ pg_lock_status _null_ _null_ _null_ )); DESCR("view system lock information"); DATA(insert OID = 2561 ( pg_blocking_pids PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 1007 "23" _null_ _null_ _null_ _null_ _null_ pg_blocking_pids _null_ _null_ _null_ )); diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index dc7d257..893767f 100644 *** a/src/include/libpq/hba.h --- b/src/include/libpq/hba.h *************** *** 16,25 **** #include "regex/regex.h" typedef enum UserAuth { uaReject, ! uaImplicitReject, uaTrust, uaIdent, uaPassword, --- 16,31 ---- #include "regex/regex.h" + /* + * The following enum represents the authentication methods that + * are supported by PostgreSQL. + * + * Note: keep this in sync with the UserAuthName array in hba.c. + */ typedef enum UserAuth { uaReject, ! uaImplicitReject, /* Not a user-visible option */ uaTrust, uaIdent, uaPassword, diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 60abcad..de5ae00 100644 *** a/src/test/regress/expected/rules.out --- b/src/test/regress/expected/rules.out *************** pg_group| SELECT pg_authid.rolname AS gr *** 1338,1343 **** --- 1338,1353 ---- 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, + a.type, + a.database, + a.user_name, + a.address, + a.netmask, + 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_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname,
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers