diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 4930506..d4aa69c 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -7555,6 +7555,11 @@ + pg_hba_rules + summary of client authentication configuration file contents + + + pg_group groups of database users @@ -8098,6 +8103,116 @@ + + <structname>pg_hba_rules</structname> + + + pg_hba_rules + + + + The view pg_hba_rules provides a summary of + the contents of the client authentication configuration file. A row + appears in this view for each entry appearing in the file, with annotations + indicating whether the rule could be applied successfully. + + + + <structname>pg_hba_rules</> Columns + + + + + Name + Type + Description + + + + + line_number + integer + + Line number of the client authentication rule in + pg_hba.conf file + + + + type + text + Type of connection + + + database + text[] + List of database names + + + user_name + text[] + List of user names + + + address + text + + Address specifies the set of hosts the record matches. + It can be a host name, or it is made up of an IP address + or keywords such as (all, + samehost and samenet). + + + + netmask + text + Address mask if exist + + + auth_method + text + Authentication method + + + options + text[] + Configuration options set for authentication method + + + error + text + + If not null, an error message indicating why this + rule could not be loaded. + + + + +
+ + + If the configuration file contains any problems, error field + indicating the problem of that rule. Following is the sample output of the view. + + + +SELECT line_number, type, database, user_name, auth_method FROM pg_hba_rules; + + + + line_number | type | database | user_name | auth_method +-------------+-------+----------+-----------+------------- + 84 | local | {all} | {all} | trust + 86 | host | {all} | {all} | trust + 88 | host | {all} | {all} | trust +(3 rows) + + + + See for more information about the various + ways to change client authentication configuration. + +
+ <structname>pg_group</structname> diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index dda5891..f20486c 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -54,6 +54,13 @@ database user names and OS user names. + + The system view + pg_hba_rules + can be helpful for pre-testing changes to the client authentication configuration file, or for + diagnosing problems if loading of file did not have the desired effects. + + The <filename>pg_hba.conf</filename> File diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 07f291b..f7de2a6 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -450,6 +450,12 @@ CREATE VIEW pg_file_settings AS REVOKE ALL on pg_file_settings FROM PUBLIC; REVOKE EXECUTE ON FUNCTION pg_show_all_file_settings() FROM PUBLIC; +CREATE VIEW pg_hba_rules AS + SELECT * FROM pg_hba_rules() AS A; + +REVOKE ALL on pg_hba_rules FROM PUBLIC; +REVOKE EXECUTE ON FUNCTION pg_hba_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 07f046f..ba73b28 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -25,15 +25,22 @@ #include #include +#include "access/htup_details.h" +#include "catalog/objectaddress.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 "storage/ipc.h" #include "utils/acl.h" +#include "utils/builtins.h" #include "utils/guc.h" #include "utils/lsyscache.h" #include "utils/memutils.h" @@ -75,6 +82,15 @@ typedef struct HbaToken bool quoted; } HbaToken; + +/* Context to use with fill_hba_line function. */ +typedef struct FillHbaLineCxt +{ + MemoryContext memcxt; + TupleDesc tupdesc; + Tuplestorestate *tuple_store; +} FillHbaLineCxt; + /* * pre-parsed content of HBA config file: list of HbaLine structs. * parsed_hba_context is the memory context where it lives. @@ -93,13 +109,19 @@ static MemoryContext parsed_hba_context = NULL; static List *parsed_ident_lines = NIL; static MemoryContext parsed_ident_context = NULL; - +static void tokenize_file_failure_callback(int code, Datum arg); static MemoryContext tokenize_file(const char *filename, FILE *file, List **lines, List **line_nums, List **raw_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); + int line_num, int level, char **err_msg); +static Datum getauthmethod(UserAuth auth_method); +static void fill_hba_auth_opt(Datum *optdata, char *optname, char *optvalue); +static Datum gethba_options(HbaLine *hba); +static void fill_hba_line(FillHbaLineCxt *context, int lineno, + HbaLine *hba, const char *err_msg); +static void fill_hba(FillHbaLineCxt *context); /* * isblank() exists in the ISO C99 spec, but it's not very portable yet, @@ -336,8 +358,12 @@ tokenize_inc_file(List *tokens, return tokens; } - /* There is possible recursion here if the file contains @ */ - linecxt = tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums, NULL); + PG_ENSURE_ERROR_CLEANUP(tokenize_file_failure_callback, PointerGetDatum(inc_file)); + { + /* There is possible recursion here if the file contains @ */ + linecxt = tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums, NULL); + } + PG_END_ENSURE_ERROR_CLEANUP(tokenize_file_failure_callback, PointerGetDatum(inc_file)); FreeFile(inc_file); pfree(inc_fullname); @@ -365,6 +391,15 @@ tokenize_inc_file(List *tokens, return tokens; } +/* Error cleanup callback for tokenize_file */ +static void +tokenize_file_failure_callback(int code, Datum arg) +{ + FILE *file = (FILE *) DatumGetPointer(arg); + + FreeFile(file); +} + /* * Tokenize the given file, storing the resulting data into three Lists: a * List of lines, a List of line numbers, and a List of raw line contents. @@ -748,11 +783,12 @@ check_same_host_or_net(SockAddr *raddr, IPCompareMethod method) * reporting error if it's not. */ #define INVALID_AUTH_OPTION(optname, validmethods) do {\ - ereport(LOG, \ + *err_msg = psprintf(_("authentication option \"%s\" is only valid for authentication methods %s"), \ + optname, _(validmethods)); \ + ereport(level, \ (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)), \ + errmsg("%s", *err_msg), \ errcontext("line %d of configuration file \"%s\"", \ line_num, HbaFileName))); \ return false; \ @@ -765,10 +801,11 @@ check_same_host_or_net(SockAddr *raddr, IPCompareMethod method) #define MANDATORY_AUTH_ARG(argvar, argname, authname) do {\ if (argvar == NULL) {\ - ereport(LOG, \ + *err_msg = psprintf(_("authentication method \"%s\" requires argument \"%s\" to be set"), \ + authname, argname); \ + ereport(level, \ (errcode(ERRCODE_CONFIG_FILE_ERROR), \ - errmsg("authentication method \"%s\" requires argument \"%s\" to be set", \ - authname, argname), \ + errmsg("%s", *err_msg), \ errcontext("line %d of configuration file \"%s\"", \ line_num, HbaFileName))); \ return NULL; \ @@ -818,7 +855,8 @@ check_same_host_or_net(SockAddr *raddr, IPCompareMethod method) * NULL. */ static HbaLine * -parse_hba_line(List *line, int line_num, char *raw_line) +parse_hba_line(List *line, int line_num, char *raw_line, + int level, char **err_msg) { char *str; struct addrinfo *gai_result; @@ -841,9 +879,10 @@ parse_hba_line(List *line, int line_num, char *raw_line) tokens = lfirst(field); if (tokens->length > 1) { - ereport(LOG, + *err_msg = pstrdup(_("multiple values specified for connection type")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("multiple values specified for connection type"), + errmsg("%s", *err_msg), errhint("Specify exactly one connection type per line."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); @@ -855,9 +894,10 @@ parse_hba_line(List *line, int line_num, char *raw_line) #ifdef HAVE_UNIX_SOCKETS parsedline->conntype = ctLocal; #else - ereport(LOG, + *err_msg = pstrdup(_("local connections are not supported by this build")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("local connections are not supported by this build"), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -874,16 +914,20 @@ parse_hba_line(List *line, int line_num, char *raw_line) /* Log a warning if SSL support is not active */ #ifdef USE_SSL if (!EnableSSL) - ereport(LOG, + { + *err_msg = pstrdup(_("hostssl record cannot match because SSL is disabled")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("hostssl record cannot match because SSL is disabled"), + errmsg("%s", *err_msg), errhint("Set ssl = on in postgresql.conf."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); + } #else - ereport(LOG, + *err_msg = pstrdup(_("hostssl record cannot match because SSL is not supported by this build")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("hostssl record cannot match because SSL is not supported by this build"), + errmsg("%s", *err_msg), errhint("Compile with --with-openssl to use SSL connections."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); @@ -901,10 +945,11 @@ parse_hba_line(List *line, int line_num, char *raw_line) } /* record type */ else { - ereport(LOG, + *err_msg = psprintf(_("invalid connection type \"%s\""), + token->string); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("invalid connection type \"%s\"", - token->string), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -914,9 +959,10 @@ parse_hba_line(List *line, int line_num, char *raw_line) field = lnext(field); if (!field) { - ereport(LOG, + *err_msg = pstrdup(_("end-of-line before database specification")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("end-of-line before database specification"), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -933,9 +979,10 @@ parse_hba_line(List *line, int line_num, char *raw_line) field = lnext(field); if (!field) { - ereport(LOG, + *err_msg = pstrdup(_("end-of-line before role specification")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("end-of-line before role specification"), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -954,9 +1001,10 @@ parse_hba_line(List *line, int line_num, char *raw_line) field = lnext(field); if (!field) { - ereport(LOG, + *err_msg = pstrdup(_("end-of-line before IP address specification")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("end-of-line before IP address specification"), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -964,9 +1012,10 @@ parse_hba_line(List *line, int line_num, char *raw_line) tokens = lfirst(field); if (tokens->length > 1) { - ereport(LOG, + *err_msg = pstrdup(_("multiple values specified for host address")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("multiple values specified for host address"), + errmsg("%s", *err_msg), errhint("Specify one address range per line."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); @@ -1019,10 +1068,11 @@ parse_hba_line(List *line, int line_num, char *raw_line) parsedline->hostname = str; else { - ereport(LOG, + *err_msg = psprintf(_("invalid IP address \"%s\": %s"), + str, gai_strerror(ret)); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("invalid IP address \"%s\": %s", - str, gai_strerror(ret)), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); if (gai_result) @@ -1037,10 +1087,11 @@ parse_hba_line(List *line, int line_num, char *raw_line) { if (parsedline->hostname) { - ereport(LOG, + *err_msg = psprintf(_("specifying both host name and CIDR mask is invalid: \"%s\""), + token->string); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("specifying both host name and CIDR mask is invalid: \"%s\"", - token->string), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -1049,10 +1100,11 @@ parse_hba_line(List *line, int line_num, char *raw_line) if (pg_sockaddr_cidr_mask(&parsedline->mask, cidr_slash + 1, parsedline->addr.ss_family) < 0) { - ereport(LOG, + *err_msg = psprintf(_("invalid CIDR mask in address \"%s\""), + token->string); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("invalid CIDR mask in address \"%s\"", - token->string), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -1066,9 +1118,10 @@ parse_hba_line(List *line, int line_num, char *raw_line) field = lnext(field); if (!field) { - ereport(LOG, + *err_msg = pstrdup(_("end-of-line before netmask specification")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("end-of-line before netmask specification"), + errmsg("%s", *err_msg), errhint("Specify an address range in CIDR notation, or provide a separate netmask."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); @@ -1077,9 +1130,10 @@ parse_hba_line(List *line, int line_num, char *raw_line) tokens = lfirst(field); if (tokens->length > 1) { - ereport(LOG, + *err_msg = pstrdup(_("multiple values specified for netmask")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("multiple values specified for netmask"), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -1090,10 +1144,11 @@ parse_hba_line(List *line, int line_num, char *raw_line) &hints, &gai_result); if (ret || !gai_result) { - ereport(LOG, + *err_msg = psprintf(_("invalid IP mask \"%s\": %s"), + token->string, gai_strerror(ret)); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("invalid IP mask \"%s\": %s", - token->string, gai_strerror(ret)), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); if (gai_result) @@ -1107,9 +1162,10 @@ parse_hba_line(List *line, int line_num, char *raw_line) if (parsedline->addr.ss_family != parsedline->mask.ss_family) { - ereport(LOG, + *err_msg = pstrdup(_("IP address and mask do not match")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("IP address and mask do not match"), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -1122,9 +1178,10 @@ parse_hba_line(List *line, int line_num, char *raw_line) field = lnext(field); if (!field) { - ereport(LOG, + *err_msg = pstrdup(_("end-of-line before authentication method")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("end-of-line before authentication method"), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -1132,9 +1189,10 @@ parse_hba_line(List *line, int line_num, char *raw_line) tokens = lfirst(field); if (tokens->length > 1) { - ereport(LOG, + *err_msg = pstrdup(_("multiple values specified for authentication type")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("multiple values specified for authentication type"), + errmsg("%s", *err_msg), errhint("Specify exactly one authentication type per line."), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); @@ -1169,9 +1227,10 @@ parse_hba_line(List *line, int line_num, char *raw_line) { if (Db_user_namespace) { - ereport(LOG, + *err_msg = pstrdup(_("MD5 authentication is not supported when \"db_user_namespace\" is enabled")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled"), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -1206,10 +1265,11 @@ parse_hba_line(List *line, int line_num, char *raw_line) parsedline->auth_method = uaRADIUS; else { - ereport(LOG, + *err_msg = psprintf(_("invalid authentication method \"%s\""), + token->string); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("invalid authentication method \"%s\"", - token->string), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -1217,10 +1277,11 @@ parse_hba_line(List *line, int line_num, char *raw_line) if (unsupauth) { - ereport(LOG, + *err_msg = psprintf(_("invalid authentication method \"%s\": not supported by this build"), + token->string); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("invalid authentication method \"%s\": not supported by this build", - token->string), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -1238,9 +1299,10 @@ parse_hba_line(List *line, int line_num, char *raw_line) if (parsedline->conntype == ctLocal && parsedline->auth_method == uaGSS) { - ereport(LOG, + *err_msg = pstrdup(_("gssapi authentication is not supported on local sockets")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("gssapi authentication is not supported on local sockets"), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -1249,9 +1311,10 @@ parse_hba_line(List *line, int line_num, char *raw_line) if (parsedline->conntype != ctLocal && parsedline->auth_method == uaPeer) { - ereport(LOG, + *err_msg = pstrdup(_("peer authentication is only supported on local sockets")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("peer authentication is only supported on local sockets"), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -1266,9 +1329,10 @@ parse_hba_line(List *line, int line_num, char *raw_line) if (parsedline->conntype != ctHostSSL && parsedline->auth_method == uaCert) { - ereport(LOG, + *err_msg = pstrdup(_("cert authentication is only supported on hostssl connections")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("cert authentication is only supported on hostssl connections"), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -1315,16 +1379,18 @@ parse_hba_line(List *line, int line_num, char *raw_line) /* * Got something that's not a name=value pair. */ - ereport(LOG, + *err_msg = psprintf(_("authentication option not in name=value format: %s"), + token->string); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("authentication option not in name=value format: %s", token->string), + errmsg("%s", *err_msg), 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)) + if (!parse_hba_auth_opt(str, val, parsedline, line_num, level, err_msg)) /* parse_hba_auth_opt already logged the error message */ return NULL; pfree(str); @@ -1352,9 +1418,10 @@ parse_hba_line(List *line, int line_num, char *raw_line) parsedline->ldapbindpasswd || parsedline->ldapsearchattribute) { - ereport(LOG, + *err_msg = pstrdup(_("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, ldapsearchattribute, or ldapurl together with ldapprefix")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("cannot use ldapbasedn, ldapbinddn, ldapbindpasswd, ldapsearchattribute, or ldapurl together with ldapprefix"), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -1362,9 +1429,10 @@ parse_hba_line(List *line, int line_num, char *raw_line) } else if (!parsedline->ldapbasedn) { - ereport(LOG, + *err_msg = pstrdup(_("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("authentication method \"ldap\" requires argument \"ldapbasedn\", \"ldapprefix\", or \"ldapsuffix\" to be set"), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return NULL; @@ -1394,7 +1462,7 @@ parse_hba_line(List *line, int line_num, char *raw_line) * encounter an error. */ static bool -parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num) +parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num, int level, char **err_msg) { #ifdef USE_LDAP hbaline->ldapscope = LDAP_SCOPE_SUBTREE; @@ -1414,9 +1482,10 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num) { if (hbaline->conntype != ctHostSSL) { - ereport(LOG, + *err_msg = pstrdup(_("clientcert can only be configured for \"hostssl\" rows")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("clientcert can only be configured for \"hostssl\" rows"), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return false; @@ -1429,9 +1498,10 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num) { if (hbaline->auth_method == uaCert) { - ereport(LOG, + *err_msg = pstrdup(_("clientcert can not be set to 0 when using \"cert\" authentication")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("clientcert can not be set to 0 when using \"cert\" authentication"), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return false; @@ -1465,18 +1535,23 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num) rc = ldap_url_parse(val, &urldata); if (rc != LDAP_SUCCESS) { - ereport(LOG, + *err_msg = psprintf(_("could not parse LDAP URL \"%s\": %s"), + val, ldap_err2string(rc)); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("could not parse LDAP URL \"%s\": %s", val, ldap_err2string(rc)))); + errmsg("%s", *err_msg))); return false; } if (strcmp(urldata->lud_scheme, "ldap") != 0) { - ereport(LOG, + *err_msg = psprintf(_("unsupported LDAP URL scheme: %s"), + urldata->lud_scheme); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("unsupported LDAP URL scheme: %s", urldata->lud_scheme))); + errmsg("%s", *err_msg))); ldap_free_urldesc(urldata); + return false; } @@ -1489,17 +1564,19 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num) hbaline->ldapscope = urldata->lud_scope; if (urldata->lud_filter) { - ereport(LOG, + *err_msg = pstrdup(_("filters not supported in LDAP URLs")); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("filters not supported in LDAP URLs"))); + errmsg("%s", *err_msg))); ldap_free_urldesc(urldata); return false; } ldap_free_urldesc(urldata); #else /* not OpenLDAP */ - ereport(LOG, + *err_msg = pstrdup(_("LDAP URLs not supported on this platform")); + ereport(level, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("LDAP URLs not supported on this platform"))); + errmsg("%s", *err_msg))); #endif /* not OpenLDAP */ } else if (strcmp(name, "ldaptls") == 0) @@ -1521,9 +1598,10 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num) hbaline->ldapport = atoi(val); if (hbaline->ldapport == 0) { - ereport(LOG, + *err_msg = psprintf(_("invalid LDAP port number: \"%s\""), val); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("invalid LDAP port number: \"%s\"", val), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return false; @@ -1609,10 +1687,11 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num) ret = pg_getaddrinfo_all(val, NULL, &hints, &gai_result); if (ret || !gai_result) { - ereport(LOG, + *err_msg = psprintf(_("could not translate RADIUS server name \"%s\" to address: %s"), + val, gai_strerror(ret)); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("could not translate RADIUS server name \"%s\" to address: %s", - val, gai_strerror(ret)), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); if (gai_result) @@ -1628,9 +1707,10 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num) hbaline->radiusport = atoi(val); if (hbaline->radiusport == 0) { - ereport(LOG, + *err_msg = psprintf(_("invalid RADIUS port number: \"%s\""), val); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("invalid RADIUS port number: \"%s\"", val), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return false; @@ -1648,10 +1728,11 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num) } else { - ereport(LOG, + *err_msg = psprintf(_("unrecognized authentication option name: \"%s\""), + name); + ereport(level, (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("unrecognized authentication option name: \"%s\"", - name), + errmsg("%s", *err_msg), errcontext("line %d of configuration file \"%s\"", line_num, HbaFileName))); return false; @@ -1790,7 +1871,11 @@ load_hba(void) return false; } - linecxt = tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums, &hba_raw_lines); + PG_ENSURE_ERROR_CLEANUP(tokenize_file_failure_callback, PointerGetDatum(file)); + { + linecxt = tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums, &hba_raw_lines); + } + PG_END_ENSURE_ERROR_CLEANUP(tokenize_file_failure_callback, PointerGetDatum(file)); FreeFile(file); /* Now parse all the lines */ @@ -1802,8 +1887,9 @@ load_hba(void) forthree(line, hba_lines, line_num, hba_line_nums, raw_line, hba_raw_lines) { HbaLine *newline; + char *err_msg = NULL; - if ((newline = parse_hba_line(lfirst(line), lfirst_int(line_num), lfirst(raw_line))) == NULL) + if ((newline = parse_hba_line(lfirst(line), lfirst_int(line_num), lfirst(raw_line), LOG, &err_msg)) == NULL) { /* * Parse error in the file, so indicate there's a problem. NB: a @@ -2166,7 +2252,11 @@ load_ident(void) return false; } - linecxt = tokenize_file(IdentFileName, file, &ident_lines, &ident_line_nums, NULL); + PG_ENSURE_ERROR_CLEANUP(tokenize_file_failure_callback, PointerGetDatum(file)); + { + linecxt = tokenize_file(IdentFileName, file, &ident_lines, &ident_line_nums, NULL); + } + PG_END_ENSURE_ERROR_CLEANUP(tokenize_file_failure_callback, PointerGetDatum(file)); FreeFile(file); /* Now parse all the lines */ @@ -2255,3 +2345,456 @@ hba_getauthmethod(hbaPort *port) { check_hba(port); } + +/* + * Returns the Text Datum representation of authentication method + */ +static Datum +getauthmethod(UserAuth auth_method) +{ + Datum result; + + switch (auth_method) + { + case uaReject: + result = CStringGetTextDatum("reject"); + break; + case uaImplicitReject: + + /* + * This case is not possible, as user cannot provide this option + * in pg_hba.conf file. But to find out any new additions to the + * enum by the compiler all the enum values are added here. + */ + Assert(0); + break; + case uaTrust: + result = CStringGetTextDatum("trust"); + break; + case uaIdent: + result = CStringGetTextDatum("ident"); + break; + case uaPassword: + result = CStringGetTextDatum("password"); + break; + case uaMD5: + result = CStringGetTextDatum("md5"); + break; + case uaGSS: + result = CStringGetTextDatum("gss"); + break; + case uaSSPI: + result = CStringGetTextDatum("sspi"); + break; + case uaPAM: + result = CStringGetTextDatum("pam"); + break; + case uaBSD: + result = CStringGetTextDatum("bsd"); + break; + case uaLDAP: + result = CStringGetTextDatum("ldap"); + break; + case uaCert: + result = CStringGetTextDatum("cert"); + break; + case uaRADIUS: + result = CStringGetTextDatum("radius"); + break; + case uaPeer: + result = CStringGetTextDatum("peer"); + break; + } + + return result; +} + +static void +fill_hba_auth_opt(Datum *optdata, char *optname, char *optvalue) +{ + StringInfoData str; + + initStringInfo(&str); + appendStringInfoString(&str, optname); + appendStringInfoString(&str, optvalue); + *optdata = CStringGetTextDatum(str.data); +} + +/* LDAP supports 10 currently, keep this well above the most any method needs */ +#define MAX_OPTIONS 12 + +static Datum +gethba_options(HbaLine *hba) +{ + int noptions; + Datum options[MAX_OPTIONS]; + char buffer[64]; + + 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) + fill_hba_auth_opt(&options[noptions++], "krb_realm=", hba->krb_realm); + } + + if (hba->usermap) + fill_hba_auth_opt(&options[noptions++], "map=", hba->usermap); + + if (hba->clientcert) + options[noptions++] = CStringGetTextDatum("clientcert=true"); + + if (hba->pamservice) + fill_hba_auth_opt(&options[noptions++], "pamservice=", hba->pamservice); + + if (hba->auth_method == uaLDAP) + { + if (hba->ldapserver) + fill_hba_auth_opt(&options[noptions++], "ldapserver=", hba->ldapserver); + + if (hba->ldapport) + { + snprintf(buffer, sizeof(buffer), "%d", hba->ldapport); + fill_hba_auth_opt(&options[noptions++], "ldapport=", buffer); + } + + if (hba->ldaptls) + options[noptions++] = CStringGetTextDatum("ldaptls=true"); + + if (hba->ldapprefix) + fill_hba_auth_opt(&options[noptions++], "ldapprefix=", hba->ldapprefix); + + if (hba->ldapsuffix) + fill_hba_auth_opt(&options[noptions++], "ldapsuffix=", hba->ldapsuffix); + + if (hba->ldapbasedn) + fill_hba_auth_opt(&options[noptions++], "ldapbasedn=", hba->ldapbasedn); + + if (hba->ldapbinddn) + fill_hba_auth_opt(&options[noptions++], "ldapbinddn=", hba->ldapbinddn); + + if (hba->ldapbindpasswd) + fill_hba_auth_opt(&options[noptions++], "ldapbindpasswd=", hba->ldapbindpasswd); + + if (hba->ldapsearchattribute) + fill_hba_auth_opt(&options[noptions++], "ldapsearchattribute=", hba->ldapsearchattribute); + + if (hba->ldapscope) + { + snprintf(buffer, sizeof(buffer), "%d", hba->ldapscope); + fill_hba_auth_opt(&options[noptions++], "ldapscope=", buffer); + } + } + + if (hba->auth_method == uaRADIUS) + { + if (hba->radiusserver) + fill_hba_auth_opt(&options[noptions++], "radiusserver=", hba->radiusserver); + + if (hba->radiussecret) + fill_hba_auth_opt(&options[noptions++], "radiussecret=", hba->radiussecret); + + if (hba->radiusidentifier) + fill_hba_auth_opt(&options[noptions++], "radiusidentifier=", hba->radiusidentifier); + + if (hba->radiusport) + { + snprintf(buffer, sizeof(buffer), "%d", hba->radiusport); + fill_hba_auth_opt(&options[noptions++], "radiusport=", buffer); + } + } + + Assert(noptions <= MAX_OPTIONS); + if (noptions) + return PointerGetDatum( + construct_array(options, noptions, TEXTOID, -1, false, 'i')); + return PointerGetDatum(NULL); +} + +#define NUM_PG_HBA_LOOKUP_ATTS 9 + +static void +fill_hba_line(FillHbaLineCxt *context, int lineno, HbaLine *hba, const char *err_msg) +{ + Datum values[NUM_PG_HBA_LOOKUP_ATTS]; + bool nulls[NUM_PG_HBA_LOOKUP_ATTS]; + ListCell *dbcell; + char buffer[NI_MAXHOST]; + HeapTuple tuple; + int index; + Datum options; + MemoryContext old_cxt; + + index = 0; + memset(values, 0, sizeof(values)); + memset(nulls, 0, sizeof(nulls)); + + old_cxt = MemoryContextSwitchTo(context->memcxt); + + /* line_number */ + values[index] = Int32GetDatum(lineno); + + if (err_msg) + { + /* set all remaining columns as NULL, except error column */ + memset(&nulls[1], true, (NUM_PG_HBA_LOOKUP_ATTS - 2)); + + /* error */ + values[NUM_PG_HBA_LOOKUP_ATTS - 1] = CStringGetTextDatum(err_msg); + } + else + { + /* type */ + index++; + switch (hba->conntype) + { + case ctLocal: + values[index] = CStringGetTextDatum("local"); + break; + case ctHost: + values[index] = CStringGetTextDatum("host"); + break; + case ctHostSSL: + values[index] = CStringGetTextDatum("hostssl"); + break; + case ctHostNoSSL: + values[index] = CStringGetTextDatum("hostnossl"); + break; + } + + /* database */ + index++; + if (list_length(hba->databases) != 0) + { + List *names = NULL; + HbaToken *tok; + + foreach(dbcell, hba->databases) + { + tok = lfirst(dbcell); + names = lappend(names, tok->string); + } + + /* database */ + Assert(names != NULL); + values[index] = PointerGetDatum(strlist_to_textarray(names)); + } + else + nulls[index] = true; + + /* user */ + index++; + if (list_length(hba->roles) != 0) + { + List *roles = NULL; + HbaToken *tok; + + foreach(dbcell, hba->roles) + { + tok = lfirst(dbcell); + roles = lappend(roles, tok->string); + } + + /* user */ + Assert(roles != NULL); + values[index] = PointerGetDatum(strlist_to_textarray(roles)); + } + else + nulls[index] = true; + + + /* address */ + index++; + switch (hba->ip_cmp_method) + { + case ipCmpMask: + if (hba->hostname) + { + values[index] = CStringGetTextDatum(hba->hostname); + nulls[++index] = true; + } + else + { + if (pg_getnameinfo_all(&hba->addr, sizeof(struct sockaddr_storage), + buffer, sizeof(buffer), + NULL, 0, + NI_NUMERICHOST) == 0) + { + clean_ipv6_addr(hba->addr.ss_family, buffer); + values[index] = CStringGetTextDatum(buffer); + } + else + nulls[index] = true; + + /* netmask */ + if (pg_getnameinfo_all(&hba->mask, sizeof(struct sockaddr_storage), + buffer, sizeof(buffer), + NULL, 0, + NI_NUMERICHOST) == 0) + { + clean_ipv6_addr(hba->mask.ss_family, buffer); + values[++index] = CStringGetTextDatum(buffer); + } + else + nulls[++index] = true; + } + break; + case ipCmpAll: + values[index] = CStringGetTextDatum("all"); + nulls[++index] = true; + break; + case ipCmpSameHost: + values[index] = CStringGetTextDatum("samehost"); + nulls[++index] = true; + break; + case ipCmpSameNet: + values[index] = CStringGetTextDatum("samenet"); + nulls[++index] = true; + break; + } + + /* auth_method */ + index++; + values[index] = getauthmethod(hba->auth_method); + + /* options */ + index++; + options = gethba_options(hba); + if (options) + values[index] = PointerGetDatum(options); + else + nulls[index] = true; + + /* error */ + index++; + nulls[index] = true; + } + + tuple = heap_form_tuple(context->tupdesc, values, nulls); + tuplestore_puttuple(context->tuple_store, tuple); + + MemoryContextSwitchTo(old_cxt); + return; +} + +/* + * Read the config file and fill the HbaLine records for the view. + */ +static void +fill_hba(FillHbaLineCxt *context) +{ + FILE *file; + List *hba_lines = NIL; + List *hba_line_nums = NIL; + List *hba_raw_lines = NIL; + ListCell *line, + *line_num, + *raw_line; + MemoryContext linecxt; + MemoryContext oldcxt; + MemoryContext hbacxt; + + file = AllocateFile(HbaFileName, "r"); + if (file == NULL) + { + ereport(LOG, + (errcode_for_file_access(), + errmsg("could not open configuration file \"%s\": %m", + HbaFileName))); + return; + } + + PG_ENSURE_ERROR_CLEANUP(tokenize_file_failure_callback, PointerGetDatum(file)); + { + linecxt = tokenize_file(HbaFileName, file, &hba_lines, &hba_line_nums, &hba_raw_lines); + } + PG_END_ENSURE_ERROR_CLEANUP(tokenize_file_failure_callback, PointerGetDatum(file)); + FreeFile(file); + + /* Now parse all the lines */ + hbacxt = AllocSetContextCreate(CurrentMemoryContext, + "hba parser context", + ALLOCSET_SMALL_SIZES); + oldcxt = MemoryContextSwitchTo(hbacxt); + forthree(line, hba_lines, line_num, hba_line_nums, raw_line, hba_raw_lines) + { + HbaLine *newline; + int lineno = lfirst_int(line_num); + char *err_msg = NULL; + + MemoryContextReset(hbacxt); + newline = parse_hba_line(lfirst(line), lineno, lfirst(raw_line), DEBUG3, &err_msg); + fill_hba_line(context, lineno, newline, err_msg); + } + + /* Free tokenizer memory */ + MemoryContextDelete(linecxt); + MemoryContextSwitchTo(oldcxt); + MemoryContextDelete(hbacxt); +} + +/* + * SQL-accessible SRF to return all the settings from the pg_hba.conf + * file. + */ +Datum +hba_rules(PG_FUNCTION_ARGS) +{ + Tuplestorestate *tuple_store; + TupleDesc tupdesc; + MemoryContext old_cxt; + FillHbaLineCxt *mycxt; + ReturnSetInfo *rsi; + + /* + * We must use the Materialize mode to be safe against HBA file reloads + * 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); + + mycxt = (FillHbaLineCxt *) palloc(sizeof(FillHbaLineCxt)); + mycxt->memcxt = AllocSetContextCreate(CurrentMemoryContext, + "pg_hba_lookup tuple cxt", + ALLOCSET_DEFAULT_SIZES); + mycxt->tupdesc = tupdesc; + mycxt->tuple_store = tuple_store; + + fill_hba(mycxt); + + MemoryContextDelete(mycxt->memcxt); + pfree(mycxt); + + PG_RETURN_NULL(); +} diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 03f55a1..9eea9c7 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3074,6 +3074,8 @@ DATA(insert OID = 2084 ( pg_show_all_settings PGNSP PGUID 12 1 1000 0 0 f f f f 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_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_ hba_rules _null_ _null_ _null_ )); +DESCR("show pg_hba config 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/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index add6adc..858dd06 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1338,6 +1338,16 @@ 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_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_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, diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 993880d..668d46a 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -604,6 +604,7 @@ FileFdwExecutionState FileFdwPlanState FileName FileNameMap +FillHbaLineCxt FindSplitData FixedParallelState FixedParamState