pg_hba_lookup function to get all matching pg_hba.conf entries

Started by Haribabu Kommiover 10 years ago69 messages
#1Haribabu Kommi
kommi.haribabu@gmail.com
2 attachment(s)

Hi Hackers,

This is the patch adds a new function called pg_hba_lookup to get all
matching entries
from the pg_hba.conf for the providing input data.The rows are
displayed from the other
the hba conf entries are matched.

This is an updated version of previous failure attempt to create a
catalog view for the
pg_hba.conf [1]/messages/by-id/F40B0968DB0A904DA78A924E633BE78645FE29@SYDEXCHTMP2.au.fjanz.com. The view is not able to handle the SQL queries properly because
keywords that are present in database and user columns.

currently the following two types are added:

pg_hba_lookup(database, user)
pg_hba_lookup(database, user, address, hostname)

How it works:

With the provided input data, it tries to match the entries of
pg_hba.conf and populate the
result set with all matching entries.

With the recent Tomlane's commit
1e24cf645d24aab3ea39a9d259897fd0cae4e4b6 of "Don't leave pg_hba and
pg_ident data lying around in running backends" [2]/messages/by-id/E1ZAQuy-00072J-7G@gemulon.postgresql.org, the parsed hba
conf entries are not available in the backend side. Temporarily I just
reverted this patch for the
proof of concept purpose. Once we agree with the approach, I will try
to find out a solution
to the same.

How is it useful:

With the output of this view, administrator can identify the lines
that are matching for the given
criteria easily without going through the file.

Record format:

Column name | datatype
-------------------------------
line_number | int4
type | text
database | jsonb
user | jsonb
address | inet
hostname | text
method | text
options | jsonb

Please suggest me for any column additions or data type changes that
are required.

Example output:

postgres=# select pg_hba_lookup('postgres','all');
pg_hba_lookup
-------------------------------------------------------
(84,local,"[""all""]","[""all""]",,,trust,{})
(86,host,"[""all""]","[""all""]",127.0.0.1,,trust,{})
(88,host,"[""all""]","[""all""]",::1,,trust,{})

Here I attached a proof of concept patch for the same.

Any suggestions/comments on this proposed approach?

[1]: /messages/by-id/F40B0968DB0A904DA78A924E633BE78645FE29@SYDEXCHTMP2.au.fjanz.com

[2]: /messages/by-id/E1ZAQuy-00072J-7G@gemulon.postgresql.org

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_hba_lookup_poc.patchapplication/octet-stream; name=pg_hba_lookup_poc.patchDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 16484,16489 **** SELECT collation for ('foo' COLLATE "de_DE");
--- 16484,16502 ----
         <entry><type>text</type></entry>
         <entry>set parameter and return new value</entry>
        </row>
+ 	  <row>
+        <entry>
+         <indexterm>
+          <primary>pg_hba_lookup</primary>
+         </indexterm>
+         <literal><function>pg_hba_lookup(<parameter>database</> <type>text</>,
+ 							<parameter>user</> <type>text</>
+ 							[, <parameter>address</> <type>text</>]
+ 							[, <parameter>hostname</> <type>text</>)</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>Returns all matching row entries of pg_hba.conf</entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
***************
*** 16541,16546 **** SELECT set_config('log_statement_stats', 'off', false);
--- 16554,16575 ----
  </programlisting>
     </para>
  
+    <para>
+     <function>pg_hba_lookup</function> returns a set of records
+ 	containing the line number, type, database, user, address,
+ 	hostname, method and options that are satisfied with the given
+ 	input values. Typical usages include:
+ <programlisting>
+ SELECT pg_hba_lookup('test_database', 'test_user');
+ 
+ 					pg_hba_lookup
+ --------------------------------------------------------------
+  (84,local,"[""all""]","[""all""]",,,trust,{})
+  (86,local,"[""test_database""]","[""test_user""]",,,trust,{})
+ (1 row)
+ </programlisting>
+    </para>
+    
    </sect2>
  
    <sect2 id="functions-admin-signal">
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 25,39 ****
--- 25,45 ----
  #include <arpa/inet.h>
  #include <unistd.h>
  
+ #include "access/htup_details.h"
  #include "catalog/pg_collation.h"
+ #include "catalog/pg_type.h"
+ #include "funcapi.h"
  #include "libpq/ip.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/jsonb.h"
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  
***************
*** 74,79 **** typedef struct HbaToken
--- 80,88 ----
  	bool		quoted;
  } HbaToken;
  
+ /* Flag to indicate the failure of reloading pg_hba.conf file */
+ bool		load_hba_failure = false;
+ 
  /*
   * pre-parsed content of HBA config file: list of HbaLine structs.
   * parsed_hba_context is the memory context where it lives.
***************
*** 100,105 **** static List *tokenize_inc_file(List *tokens, const char *outer_filename,
--- 109,119 ----
  static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
  				   int line_num);
  
+ static void hba_getvalues_for_line(HbaLine *hba, Datum *values, bool *nulls);
+ static bool pg_hba_match(HbaLine *hba, char *user, char *database, char *address, char *hostname);
+ static void pg_hba_lookup_internal(char	*user, char	*database, char	*address,
+ 					char *hostname, ReturnSetInfo *rsi);
+ 
  /*
   * isblank() exists in the ISO C99 spec, but it's not very portable yet,
   * so provide our own version.
***************
*** 2233,2235 **** hba_getauthmethod(hbaPort *port)
--- 2247,2822 ----
  {
  	check_hba(port);
  }
+ 
+ 
+ /* LDAP supports 10 currently, keep this well above the most any method needs */
+ #define MAX_OPTIONS 12
+ 
+ /*
+  * Fill in suitable values to build a tuple representing the
+  * HbaLine provided
+  */
+ static void
+ hba_getvalues_for_line(HbaLine *hba, Datum *values, bool *nulls)
+ {
+ 	ListCell   *dbcell;
+ 	char		buffer[NI_MAXHOST];
+ 	int			index = 0;
+ 	JsonbInState result;
+ 	JsonbValue	v;
+ 	char	   *str;
+ 	int			len;
+ 
+ 	/* line_number */
+ 	values[index] = Int32GetDatum(hba->linenumber);
+ 
+ 	/* 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;
+  		default:
+  			elog(ERROR, "unexpected connection type in parsed HBA entry");
+  			break;
+  	}
+ 
+ 	/* database */
+ 	index++;
+ 	memset(&result, 0, sizeof(JsonbInState));
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
+ 
+ 	if (list_length(hba->databases) != 0)
+ 	{
+ 		HbaToken   *tok;
+ 
+ 		foreach(dbcell, hba->databases)
+ 		{
+ 			tok = lfirst(dbcell);
+ 			add_jsonb(CStringGetTextDatum(tok->string), false, &result, TEXTOID, false);
+ 		}
+ 	}
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
+ 	values[index] = PointerGetDatum(JsonbValueToJsonb(result.res));
+ 
+ 	/* user */
+ 	index++;
+ 	memset(&result, 0, sizeof(JsonbInState));
+ 	result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
+ 
+ 	if (list_length(hba->roles) != 0)
+ 	{
+ 		HbaToken   *tok;
+ 
+ 		foreach(dbcell, hba->roles)
+ 		{
+ 			tok = lfirst(dbcell);
+ 			add_jsonb(CStringGetTextDatum(tok->string), false, &result, TEXTOID, false);
+ 		}
+ 	}
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
+ 	values[index] = PointerGetDatum(JsonbValueToJsonb(result.res));
+ 
+ 	/* address */
+ 	index++;
+ 	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+ 	}
+ 	else
+ 		nulls[index] = true;
+ 
+ 	/* hostname */
+ 	index++;
+ 	if (hba->hostname)
+ 		values[index] = CStringGetTextDatum(hba->hostname);
+ 	else
+ 		nulls[index] = true;
+ 
+ 	/* method */
+  	index++;
+  	switch (hba->auth_method)
+  	{
+  		case uaReject:
+  			values[index] = CStringGetTextDatum("reject");
+  			break;
+  		case uaTrust:
+  			values[index] = CStringGetTextDatum("trust");
+  			break;
+  		case uaIdent:
+  			values[index] = CStringGetTextDatum("ident");
+  			break;
+  		case uaPassword:
+  			values[index] = CStringGetTextDatum("password");
+  			break;
+  		case uaMD5:
+  			values[index] = CStringGetTextDatum("md5");
+  			break;
+  		case uaGSS:
+  			values[index] = CStringGetTextDatum("gss");
+  			break;
+  		case uaSSPI:
+  			values[index] = CStringGetTextDatum("sspi");
+  			break;
+  		case uaPAM:
+  			values[index] = CStringGetTextDatum("pam");
+  			break;
+  		case uaLDAP:
+  			values[index] = CStringGetTextDatum("ldap");
+  			break;
+  		case uaCert:
+  			values[index] = CStringGetTextDatum("cert");
+  			break;
+  		case uaRADIUS:
+  			values[index] = CStringGetTextDatum("radius");
+  			break;
+  		case uaPeer:
+  			values[index] = CStringGetTextDatum("peer");
+  			break;
+  		default:
+  			elog(ERROR, "unexpected authentication method in parsed HBA entry");
+  			break;
+  	}
+ 
+ 	/* options */
+ 	index++;
+ 	memset(&result, 0, sizeof(JsonbInState));
+ 	result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
+ 
+ 	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
+ 	{
+ 		if (hba->include_realm)
+ 		{
+ 			str = pstrdup("include_realm");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(BoolGetDatum(true), false, &result, BOOLOID, false);
+ 		}
+ 
+ 		if (hba->krb_realm)
+ 		{
+ 			str = pstrdup("krb_realm");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->krb_realm), false, &result, TEXTOID, false);
+ 		}
+ 	}
+ 
+ 	if (hba->usermap)
+ 	{
+ 		str = pstrdup("map");
+ 		len = strlen(str);
+ 
+ 		v.type = jbvString;
+ 
+ 		v.val.string.len = len;
+ 		v.val.string.val = str;
+ 
+ 		result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 		add_jsonb(CStringGetTextDatum(hba->usermap), false, &result, TEXTOID, false);
+ 	}
+ 
+ 	if (hba->auth_method == uaLDAP)
+ 	{
+ 		if (hba->ldapserver)
+ 		{
+ 			str = pstrdup("ldapserver");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapserver), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapport)
+ 		{
+ 			str = pstrdup("ldapport");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(Int32GetDatum(hba->ldapport), false, &result, INT4OID, false);
+ 		}
+ 
+ 		if (hba->ldaptls)
+ 		{
+ 			str = pstrdup("ldaptls");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(BoolGetDatum(true), false, &result, BOOLOID, false);
+ 		}
+ 
+ 		if (hba->ldapprefix)
+ 		{
+ 			str = pstrdup("ldapprefix");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapprefix), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapsuffix)
+ 		{
+ 			str = pstrdup("ldapsuffix");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapsuffix), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapbasedn)
+ 		{
+ 			str = pstrdup("ldapbasedn");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapbasedn), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapbinddn)
+ 		{
+ 			str = pstrdup("ldapbinddn");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapbinddn), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapbindpasswd)
+ 		{
+ 			str = pstrdup("ldapbindpasswd");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapbindpasswd), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapsearchattribute)
+ 		{
+ 			str = pstrdup("ldapsearchattribute");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapsearchattribute), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapscope)
+ 		{
+ 			str = pstrdup("ldapscope");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(Int32GetDatum(hba->ldapscope), false, &result, INT4OID, false);
+ 		}
+ 	}
+ 
+ 	if (hba->auth_method == uaRADIUS)
+ 	{
+ 		if (hba->radiusserver)
+ 		{
+ 			str = pstrdup("radiusserver");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->radiusserver), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->radiussecret)
+ 		{
+ 			str = pstrdup("radiussecret");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->radiussecret), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->radiusidentifier)
+ 		{
+ 			str = pstrdup("radiusidentifier");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->radiusidentifier), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->radiusport)
+ 		{
+ 			str = pstrdup("radiusport");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(Int32GetDatum(hba->radiusport), false, &result, INT4OID, false);
+ 		}
+ 	}
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
+ 	values[index] = PointerGetDatum(JsonbValueToJsonb(result.res));
+ }
+ 
+ static bool
+ pg_hba_match(HbaLine *hba, char *user, char *database, char *address, char *hostname)
+ {
+ 	Oid			roleid;
+ 
+ 	/* Get the target role's OID.  Note we do not error out for bad role. */
+ 	roleid = get_role_oid(user, true);
+ 
+ 	if (!check_db(database, user, roleid, hba->databases))
+ 		return false;
+ 
+ 	if (!check_role(user, roleid, hba->roles))
+ 		return false;
+ 
+ 	return true;
+ }
+ 
+ #define NUM_PG_HBA_CONF_ATTS   8
+ 
+ static void
+ pg_hba_lookup_internal(char	*user, char	*database, char	*address,
+ 			char *hostname, ReturnSetInfo *rsi)
+ {
+ 	Tuplestorestate *tuple_store;
+ 	TupleDesc	tupdesc;
+ 	ListCell   *line;
+ 	MemoryContext old_cxt;
+ 
+ 	/*
+ 	* Create the tupledesc and tuplestore in the per_query context as
+ 	* required for SFRM_Materialize.
+ 	*/
+ 	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+ 
+ 	tupdesc = CreateTemplateTupleDesc(NUM_PG_HBA_CONF_ATTS, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)1, "line_number",
+ 		INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)2, "type",
+ 		TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)3, "database",
+ 		JSONBOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)4, "user",
+ 		JSONBOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)5, "address",
+ 		INETOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)6, "hostname",
+ 		TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)7, "method",
+ 		TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)8, "options",
+ 		JSONBOID, -1, 0);
+ 	BlessTupleDesc(tupdesc);
+ 
+ 	tuple_store =
+ 		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+ 		false, work_mem);
+ 
+ 	MemoryContextSwitchTo(old_cxt);
+ 
+ 	/*
+ 	* Loop through the list and deparse each entry as it comes, storing it in
+ 	* the tuplestore. Any temporary memory allocations here live only for the
+ 	* function call lifetime.
+ 	*/
+ 	foreach(line, parsed_hba_lines)
+ 	{
+ 		HbaLine    *hba = (HbaLine *)lfirst(line);
+ 		Datum		values[NUM_PG_HBA_CONF_ATTS];
+ 		bool		nulls[NUM_PG_HBA_CONF_ATTS];
+ 		HeapTuple	tuple;
+ 
+ 		MemSet(values, 0, sizeof(values));
+ 		MemSet(nulls, 0, sizeof(nulls));
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		if (pg_hba_match(hba, user, database, address, hostname))
+ 		{
+ 			/* Get the next parsed hba line values */
+ 			hba_getvalues_for_line(hba, values, nulls);
+ 
+ 			/* build a tuple */
+ 			tuple = heap_form_tuple(tupdesc, values, nulls);
+ 			tuplestore_puttuple(tuple_store, tuple);
+ 		}
+ 	}
+ 
+ 	rsi->setDesc = tupdesc;
+ 	rsi->setResult = tuple_store;
+ 	return;
+ }
+ 
+ /*
+ * SQL-accessible SRF to return all the settings from the pg_hba.conf
+ * file.
+ */
+ Datum
+ pg_hba_lookup_2args(PG_FUNCTION_ARGS)
+ {
+ 	return pg_hba_lookup(fcinfo);
+ }
+ 
+ /*
+  * SQL-accessible SRF to return all the settings from the pg_hba.conf
+  * file. 
+  */
+ Datum
+ pg_hba_lookup(PG_FUNCTION_ARGS)
+ {
+ 	char	*user;
+ 	char	*database;
+ 	char	*address;
+ 	char    *hostname;
+ 
+ 	/*
+ 	* 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.
+ 	*/
+ 	ReturnSetInfo *rsi = (ReturnSetInfo *)fcinfo->resultinfo;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("only superuser can view pg_hba.conf settings"))));
+ 
+ 	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+ 		(rsi->allowedModes & SFRM_Materialize) == 0)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				errmsg("set-valued function called in context that "
+ 						"cannot accept a set")));
+ 
+ 	if (PG_ARGISNULL(0))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 				(errmsg("user name is required to match pg_hba configuration entry"))));
+ 	else
+ 		user = TextDatumGetCString(PG_GETARG_DATUM(0));
+ 
+ 	if (PG_ARGISNULL(1))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 				(errmsg("database name is required to match pg_hba configuration entry"))));
+ 	else
+ 		database = TextDatumGetCString(PG_GETARG_DATUM(1));
+ 
+ 	if (PG_NARGS() != 2)
+ 	{
+ 		if (!PG_ARGISNULL(2))
+ 			address = TextDatumGetCString(PG_GETARG_DATUM(2));
+ 
+ 		if (!PG_ARGISNULL(3))
+ 			hostname = TextDatumGetCString(PG_GETARG_DATUM(3));
+ 	}
+ 
+ 	if (load_hba_failure)
+ 		ereport(WARNING,
+ 		(errmsg("There was some failure in reloading pg_hba.conf file. "
+ 		"The pg_hba.conf settings data may contains stale information")));
+ 
+ 	rsi->returnMode = SFRM_Materialize;
+ 
+ 	pg_hba_lookup_internal(user, database, address, hostname, rsi);
+ 
+ 	PG_RETURN_NULL();
+ }
+ 
+ 
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 3998,4003 **** PostgresMain(int argc, char *argv[],
--- 3998,4016 ----
  		{
  			got_SIGHUP = false;
  			ProcessConfigFile(PGC_SIGHUP);
+ 			
+ 			/* 
+ 			 * Reload authentication config files too to refresh 
+ 			 * pg_hba_conf view data.
+ 			 */
+ 			if (!load_hba())
+ 			{
+ 				ereport(DEBUG1,
+ 					(errmsg("Falure in reloading pg_hba.conf, pg_hba_conf view may show stale information")));
+ 				load_hba_failure = true;
+ 			}
+ 			
+ 			load_hba_failure = false;
  		}
  
  		/*
*** a/src/backend/utils/adt/jsonb.c
--- b/src/backend/utils/adt/jsonb.c
***************
*** 36,47 ****
  
  #define DT_INFINITY "infinity"
  
- typedef struct JsonbInState
- {
- 	JsonbParseState *parseState;
- 	JsonbValue *res;
- } JsonbInState;
- 
  /* unlike with json categories, we need to treat json and jsonb differently */
  typedef enum					/* type categories for datum_to_jsonb */
  {
--- 36,41 ----
***************
*** 82,88 **** static void jsonb_categorize_type(Oid typoid,
  static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
  			   JsonbTypeCategory tcategory, Oid outfuncoid,
  			   bool key_scalar);
! static void add_jsonb(Datum val, bool is_null, JsonbInState *result,
  		  Oid val_type, bool key_scalar);
  static JsonbParseState *clone_parse_state(JsonbParseState *state);
  static char *JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent);
--- 76,82 ----
  static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
  			   JsonbTypeCategory tcategory, Oid outfuncoid,
  			   bool key_scalar);
! void add_jsonb(Datum val, bool is_null, JsonbInState *result,
  		  Oid val_type, bool key_scalar);
  static JsonbParseState *clone_parse_state(JsonbParseState *state);
  static char *JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent);
***************
*** 1119,1125 **** composite_to_jsonb(Datum composite, JsonbInState *result)
   * lookups only once.
   */
  
! static void
  add_jsonb(Datum val, bool is_null, JsonbInState *result,
  		  Oid val_type, bool key_scalar)
  {
--- 1113,1119 ----
   * lookups only once.
   */
  
! void
  add_jsonb(Datum val, bool is_null, JsonbInState *result,
  		  Oid val_type, bool key_scalar)
  {
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3075,3080 **** DATA(insert OID = 2084 (  pg_show_all_settings	PGNSP PGUID 12 1 1000 0 0 f f f f
--- 3075,3084 ----
  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 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 = 3997 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v 2 0 2249 "25 25" "{25,25,23,25,3802,3802,869,25,25,3802}" "{i,i,o,o,o,o,o,o,o,o}" "{database,user,line_number,type,database,user,address,hostname,method,options}" _null_ _null_ pg_hba_lookup_2args _null_ _null_ _null_));
+ DESCR("view client authentication settings");
+ DATA(insert OID = 3998 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v 4 0 2249 "25 25 25 25" "{25,25,25,25,23,25,3802,3802,869,25,25,3802}" "{i,i,i,i,o,o,o,o,o,o,o,o}" "{database,user,address,hostname,line_number,type,database,user,address,hostname,method,options}" _null_ _null_ pg_hba_lookup _null_ _null_ _null_));
+ DESCR("view client authentication settings");
  DATA(insert OID = 1371 (  pg_lock_status   PGNSP PGUID 12 1 1000 0 0 f f f f t t v 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 = 1065 (  pg_prepared_xact PGNSP PGUID 12 1 1000 0 0 f f f f t t v 0 0 2249 "" "{28,25,1184,26,26}" "{o,o,o,o,o}" "{transaction,gid,prepared,ownerid,dbid}" _null_ _null_ pg_prepared_xact _null_ _null_ _null_ ));
*** a/src/include/libpq/hba.h
--- b/src/include/libpq/hba.h
***************
*** 96,101 **** typedef struct IdentLine
--- 96,103 ----
  /* kluge to avoid including libpq/libpq-be.h here */
  typedef struct Port hbaPort;
  
+ extern bool load_hba_failure;
+ 
  extern bool load_hba(void);
  extern bool load_ident(void);
  extern void hba_getauthmethod(hbaPort *port);
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 1118,1123 **** extern Datum show_config_by_name_missing_ok(PG_FUNCTION_ARGS);
--- 1118,1125 ----
  extern Datum set_config_by_name(PG_FUNCTION_ARGS);
  extern Datum show_all_settings(PG_FUNCTION_ARGS);
  extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
+ extern Datum pg_hba_lookup_2args(PG_FUNCTION_ARGS);
+ extern Datum pg_hba_lookup(PG_FUNCTION_ARGS);
  
  /* lockfuncs.c */
  extern Datum pg_lock_status(PG_FUNCTION_ARGS);
*** a/src/include/utils/json.h
--- b/src/include/utils/json.h
***************
*** 43,49 **** extern Datum json_object(PG_FUNCTION_ARGS);
  extern Datum json_object_two_arg(PG_FUNCTION_ARGS);
  
  extern void escape_json(StringInfo buf, const char *str);
- 
  extern Datum json_typeof(PG_FUNCTION_ARGS);
  
  /* functions in jsonfuncs.c */
--- 43,48 ----
*** a/src/include/utils/jsonb.h
--- b/src/include/utils/jsonb.h
***************
*** 343,348 **** typedef struct JsonbIterator
--- 343,354 ----
  	struct JsonbIterator *parent;
  } JsonbIterator;
  
+ typedef struct JsonbInState
+ {
+ 	JsonbParseState *parseState;
+ 	JsonbValue *res;
+ } JsonbInState;
+ 
  /* I/O routines */
  extern Datum jsonb_in(PG_FUNCTION_ARGS);
  extern Datum jsonb_out(PG_FUNCTION_ARGS);
***************
*** 428,433 **** extern bool JsonbDeepContains(JsonbIterator **val,
--- 434,441 ----
  extern void JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash);
  
  /* jsonb.c support functions */
+ void add_jsonb(Datum val, bool is_null, JsonbInState *result,
+ 	Oid val_type, bool key_scalar);
  extern char *JsonbToCString(StringInfo out, JsonbContainer *in,
  			   int estimated_len);
  extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in,
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
***************
*** 1327,1332 **** pg_group| SELECT pg_authid.rolname AS groname,
--- 1327,1344 ----
            WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist
     FROM pg_authid
    WHERE (NOT pg_authid.rolcanlogin);
+ pg_hba_conf| SELECT a.line_number,
+     a.type,
+     a.keyword_databases,
+     a.databases,
+     a.keyword_users,
+     a.users,
+     a.address,
+     a.compare_method,
+     a.hostname,
+     a.method,
+     a.options
+    FROM pg_hba_conf() a(line_number, type, keyword_databases, databases, keyword_users, users, address, compare_method, hostname, method, options);
  pg_indexes| SELECT n.nspname AS schemaname,
      c.relname AS tablename,
      i.relname AS indexname,
revert_hba_context_release_in_backend.patchapplication/octet-stream; name=revert_hba_context_release_in_backend.patchDownload
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 386,392 **** tokenize_file(const char *filename, FILE *file,
  	MemoryContext linecxt;
  	MemoryContext oldcxt;
  
! 	linecxt = AllocSetContextCreate(CurrentMemoryContext,
  									"tokenize file cxt",
  									ALLOCSET_DEFAULT_MINSIZE,
  									ALLOCSET_DEFAULT_INITSIZE,
--- 386,392 ----
  	MemoryContext linecxt;
  	MemoryContext oldcxt;
  
! 	linecxt = AllocSetContextCreate(TopMemoryContext,
  									"tokenize file cxt",
  									ALLOCSET_DEFAULT_MINSIZE,
  									ALLOCSET_DEFAULT_INITSIZE,
***************
*** 1770,1777 **** load_hba(void)
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	Assert(PostmasterContext);
! 	hbacxt = AllocSetContextCreate(PostmasterContext,
  								   "hba parser context",
  								   ALLOCSET_DEFAULT_MINSIZE,
  								   ALLOCSET_DEFAULT_MINSIZE,
--- 1770,1776 ----
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	hbacxt = AllocSetContextCreate(TopMemoryContext,
  								   "hba parser context",
  								   ALLOCSET_DEFAULT_MINSIZE,
  								   ALLOCSET_DEFAULT_MINSIZE,
***************
*** 2148,2155 **** load_ident(void)
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	Assert(PostmasterContext);
! 	ident_context = AllocSetContextCreate(PostmasterContext,
  										  "ident parser context",
  										  ALLOCSET_DEFAULT_MINSIZE,
  										  ALLOCSET_DEFAULT_MINSIZE,
--- 2147,2153 ----
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	ident_context = AllocSetContextCreate(TopMemoryContext,
  										  "ident parser context",
  										  ALLOCSET_DEFAULT_MINSIZE,
  										  ALLOCSET_DEFAULT_MINSIZE,
*** a/src/backend/utils/init/postinit.c
--- b/src/backend/utils/init/postinit.c
***************
*** 52,58 ****
  #include "utils/acl.h"
  #include "utils/fmgroids.h"
  #include "utils/guc.h"
- #include "utils/memutils.h"
  #include "utils/pg_locale.h"
  #include "utils/portal.h"
  #include "utils/ps_status.h"
--- 52,57 ----
***************
*** 191,208 **** PerformAuthentication(Port *port)
  	 * FIXME: [fork/exec] Ugh.  Is there a way around this overhead?
  	 */
  #ifdef EXEC_BACKEND
- 	/*
- 	 * load_hba() and load_ident() want to work within the PostmasterContext,
- 	 * so create that if it doesn't exist (which it won't).  We'll delete it
- 	 * again later, in PostgresMain.
- 	 */
- 	if (PostmasterContext == NULL)
- 		PostmasterContext = AllocSetContextCreate(TopMemoryContext,
- 												  "Postmaster",
- 												  ALLOCSET_DEFAULT_MINSIZE,
- 												  ALLOCSET_DEFAULT_INITSIZE,
- 												  ALLOCSET_DEFAULT_MAXSIZE);
- 
  	if (!load_hba())
  	{
  		/*
--- 190,195 ----
#2Pavel Stehule
pavel.stehule@gmail.com
In reply to: Haribabu Kommi (#1)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

Hi

Any suggestions/comments on this proposed approach?

This is interesting functionality - The same was requested by one important
Czech customer.

I'll do review

Regards

Pavel

#3Pavel Stehule
pavel.stehule@gmail.com
In reply to: Haribabu Kommi (#1)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

Hi

postgres=# select pg_hba_lookup('postgres','all');
pg_hba_lookup
-------------------------------------------------------
(84,local,"[""all""]","[""all""]",,,trust,{})
(86,host,"[""all""]","[""all""]",127.0.0.1,,trust,{})
(88,host,"[""all""]","[""all""]",::1,,trust,{})

Here I attached a proof of concept patch for the same.

Any suggestions/comments on this proposed approach?

If I understand well to your proposal, the major benefit is in
impossibility to enter pg_hba keywords - so you don't need to analyse if
parameter is keyword or not? It has sense, although It can be hard to do
image about pg_hba conf from these partial views.

There can be other way - theoretically we can have a function pg_hba_debug
with similar API like pg_hba_conf. The result will be a content of
pg_hba.conf with information about result of any rule.

Regards

Pavel

#4Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Pavel Stehule (#3)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Mon, Sep 7, 2015 at 4:34 AM, Pavel Stehule <pavel.stehule@gmail.com> wrote:

Hi

postgres=# select pg_hba_lookup('postgres','all');
pg_hba_lookup
-------------------------------------------------------
(84,local,"[""all""]","[""all""]",,,trust,{})
(86,host,"[""all""]","[""all""]",127.0.0.1,,trust,{})
(88,host,"[""all""]","[""all""]",::1,,trust,{})

Here I attached a proof of concept patch for the same.

Any suggestions/comments on this proposed approach?

If I understand well to your proposal, the major benefit is in impossibility
to enter pg_hba keywords - so you don't need to analyse if parameter is
keyword or not? It has sense, although It can be hard to do image about
pg_hba conf from these partial views.

From the function output, it is little bit difficult to map the
pg_hba.conf file.
Because of problems in processing keywords in where clause of a view, I changed
from view to function.

Is there any possibility with rule or something, that the where clause
details can be passed
as function arguments to get the data?

There can be other way - theoretically we can have a function pg_hba_debug
with similar API like pg_hba_conf. The result will be a content of
pg_hba.conf with information about result of any rule.

The output of pg_hba_debug function looks like, the entry of
pg_hba.conf and the result
match for the given input data.

Regards,
Hari Babu
Fujitsu Australia

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Pavel Stehule
pavel.stehule@gmail.com
In reply to: Haribabu Kommi (#1)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

Hi

2015-07-21 11:15 GMT+02:00 Haribabu Kommi <kommi.haribabu@gmail.com>:

Hi Hackers,

This is the patch adds a new function called pg_hba_lookup to get all
matching entries
from the pg_hba.conf for the providing input data.The rows are
displayed from the other
the hba conf entries are matched.

This is an updated version of previous failure attempt to create a
catalog view for the
pg_hba.conf [1]. The view is not able to handle the SQL queries properly
because
keywords that are present in database and user columns.

currently the following two types are added:

pg_hba_lookup(database, user)
pg_hba_lookup(database, user, address, hostname)

After testing your prototype I am thinking so it is not good way. It is
hard to understand what is result of these functions.

-1 from me

Regards

Pavel

Show quoted text

How it works:

With the provided input data, it tries to match the entries of
pg_hba.conf and populate the
result set with all matching entries.

With the recent Tomlane's commit
1e24cf645d24aab3ea39a9d259897fd0cae4e4b6 of "Don't leave pg_hba and
pg_ident data lying around in running backends" [2], the parsed hba
conf entries are not available in the backend side. Temporarily I just
reverted this patch for the
proof of concept purpose. Once we agree with the approach, I will try
to find out a solution
to the same.

How is it useful:

With the output of this view, administrator can identify the lines
that are matching for the given
criteria easily without going through the file.

Record format:

Column name | datatype
-------------------------------
line_number | int4
type | text
database | jsonb
user | jsonb
address | inet
hostname | text
method | text
options | jsonb

Please suggest me for any column additions or data type changes that
are required.

Example output:

postgres=# select pg_hba_lookup('postgres','all');
pg_hba_lookup
-------------------------------------------------------
(84,local,"[""all""]","[""all""]",,,trust,{})
(86,host,"[""all""]","[""all""]",127.0.0.1,,trust,{})
(88,host,"[""all""]","[""all""]",::1,,trust,{})

Here I attached a proof of concept patch for the same.

Any suggestions/comments on this proposed approach?

[1]
/messages/by-id/F40B0968DB0A904DA78A924E633BE78645FE29@SYDEXCHTMP2.au.fjanz.com

[2]
/messages/by-id/E1ZAQuy-00072J-7G@gemulon.postgresql.org

Regards,
Hari Babu
Fujitsu Australia

#6Peter Eisentraut
peter_e@gmx.net
In reply to: Haribabu Kommi (#1)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On 7/21/15 5:15 AM, Haribabu Kommi wrote:

With the output of this view, administrator can identify the lines
that are matching for the given
criteria easily without going through the file.

How is this useful? I could see the use if you want to debug cases of
user foo on host bar says they can't connect, but you can't impersonate
them to verify it. But then all you need is a function with a scalar
result, not a result set.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Peter Eisentraut (#6)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Mon, Nov 16, 2015 at 2:30 PM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 7/21/15 5:15 AM, Haribabu Kommi wrote:

With the output of this view, administrator can identify the lines
that are matching for the given
criteria easily without going through the file.

How is this useful? I could see the use if you want to debug cases of
user foo on host bar says they can't connect, but you can't impersonate
them to verify it. But then all you need is a function with a scalar
result, not a result set.

Do you mean the function should return true or false based on the connection
status with the provided arguments?

I also feel difficult to understand the function result as compared to a view.

Regards,
Hari Babu
Fujitsu Australia

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8Peter Eisentraut
peter_e@gmx.net
In reply to: Haribabu Kommi (#7)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On 11/16/15 2:37 AM, Haribabu Kommi wrote:

On Mon, Nov 16, 2015 at 2:30 PM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 7/21/15 5:15 AM, Haribabu Kommi wrote:

With the output of this view, administrator can identify the lines
that are matching for the given
criteria easily without going through the file.

How is this useful? I could see the use if you want to debug cases of
user foo on host bar says they can't connect, but you can't impersonate
them to verify it. But then all you need is a function with a scalar
result, not a result set.

Do you mean the function should return true or false based on the connection
status with the provided arguments?

I also feel difficult to understand the function result as compared to a view.

An hba lookup is essentially a lookup by user name, database name,
client address, yielding an authentication method (possibly with
parameters). So I think this function should work that way as well:
arguments are user name, database name, and so on, and the return value
is an authentication method. Maybe it would be some kind of record,
with line number and some parameters.

That would address the use case I put forth above. I don't know whether
that's what you were going for.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Peter Eisentraut (#8)
1 attachment(s)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Tue, Nov 17, 2015 at 9:37 AM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 11/16/15 2:37 AM, Haribabu Kommi wrote:

On Mon, Nov 16, 2015 at 2:30 PM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 7/21/15 5:15 AM, Haribabu Kommi wrote:

With the output of this view, administrator can identify the lines
that are matching for the given
criteria easily without going through the file.

How is this useful? I could see the use if you want to debug cases of
user foo on host bar says they can't connect, but you can't impersonate
them to verify it. But then all you need is a function with a scalar
result, not a result set.

Do you mean the function should return true or false based on the connection
status with the provided arguments?

I also feel difficult to understand the function result as compared to a view.

An hba lookup is essentially a lookup by user name, database name,
client address, yielding an authentication method (possibly with
parameters). So I think this function should work that way as well:
arguments are user name, database name, and so on, and the return value
is an authentication method. Maybe it would be some kind of record,
with line number and some parameters.

That would address the use case I put forth above. I don't know whether
that's what you were going for.

Thanks. Here I attached the poc patch that returns authentication method of the
first matched hba entry in pg_hba.conf with the given input values.
Currently these
functions returns text type. Based on the details required to be
printed, it can
be changed.

postgres=# select pg_hba_lookup('all', 'all');
pg_hba_lookup
---------------
trust
(1 row)

comments for the approach?

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_hba_lookup_poc_v2.patchapplication/octet-stream; name=pg_hba_lookup_poc_v2.patchDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 16576,16581 **** SELECT collation for ('foo' COLLATE "de_DE");
--- 16576,16594 ----
         <entry><type>text</type></entry>
         <entry>set parameter and return new value</entry>
        </row>
+ 	  <row>
+        <entry>
+         <indexterm>
+          <primary>pg_hba_lookup</primary>
+         </indexterm>
+         <literal><function>pg_hba_lookup(<parameter>database</> <type>text</>,
+ 							<parameter>user</> <type>text</>
+ 							[, <parameter>address</> <type>text</>]
+ 							[, <parameter>hostname</> <type>text</>)</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>Returns authentication method of the matching entry found in pg_hba.conf</entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
***************
*** 16633,16638 **** SELECT set_config('log_statement_stats', 'off', false);
--- 16646,16665 ----
  </programlisting>
     </para>
  
+    <para>
+     <function>pg_hba_lookup</function> returns first matching
+ 	entry authentication method in pg_hba.conf with the given
+ 	input values. Typical usages include:
+ <programlisting>
+ SELECT pg_hba_lookup('test_database', 'test_user');
+ 
+  pg_hba_lookup 
+ ---------------
+  trust
+ (1 row)
+ </programlisting>
+    </para>
+    
    </sect2>
  
    <sect2 id="functions-admin-signal">
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 28,38 ****
--- 28,40 ----
  #include "catalog/pg_collation.h"
  #include "libpq/ip.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"
***************
*** 74,79 **** typedef struct HbaToken
--- 76,84 ----
  	bool		quoted;
  } HbaToken;
  
+ /* Flag to indicate the failure of reloading pg_hba.conf file */
+ bool		load_hba_failure = false;
+ 
  /*
   * pre-parsed content of HBA config file: list of HbaLine structs.
   * parsed_hba_context is the memory context where it lives.
***************
*** 99,104 **** static List *tokenize_inc_file(List *tokens, const char *outer_filename,
--- 104,110 ----
  				  const char *inc_filename);
  static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
  				   int line_num);
+ static bool pg_hba_match(HbaLine *hba, char *user, char *database, char *address, char *hostname);
  
  /*
   * isblank() exists in the ISO C99 spec, but it's not very portable yet,
***************
*** 2233,2235 **** hba_getauthmethod(hbaPort *port)
--- 2239,2382 ----
  {
  	check_hba(port);
  }
+ 
+ 
+ static bool
+ pg_hba_match(HbaLine *hba, char *user, char *database, char *address, char *hostname)
+ {
+ 	Oid			roleid;
+ 
+ 	/* Get the target role's OID.  Note we do not error out for bad role. */
+ 	roleid = get_role_oid(user, true);
+ 
+ 	if (!check_db(database, user, roleid, hba->databases))
+ 		return false;
+ 
+ 	if (!check_role(user, roleid, hba->roles))
+ 		return false;
+ 
+ 	return true;
+ }
+ 
+ 
+ /*
+ * SQL-accessible SRF to return all the settings from the pg_hba.conf
+ * file.
+ */
+ Datum
+ pg_hba_lookup_2args(PG_FUNCTION_ARGS)
+ {
+ 	return pg_hba_lookup(fcinfo);
+ }
+ 
+ /*
+  * SQL-accessible SRF to return all the settings from the pg_hba.conf
+  * file. 
+  */
+ Datum
+ pg_hba_lookup(PG_FUNCTION_ARGS)
+ {
+ 	char	*user;
+ 	char	*database;
+ 	char	*address;
+ 	char    *hostname;
+ 	ListCell   *line;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("only superuser can view pg_hba.conf settings"))));
+ 
+ 	if (PG_ARGISNULL(0))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 				(errmsg("user name is required to match pg_hba configuration entry"))));
+ 	else
+ 		user = TextDatumGetCString(PG_GETARG_DATUM(0));
+ 
+ 	if (PG_ARGISNULL(1))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 				(errmsg("database name is required to match pg_hba configuration entry"))));
+ 	else
+ 		database = TextDatumGetCString(PG_GETARG_DATUM(1));
+ 
+ 	if (PG_NARGS() != 2)
+ 	{
+ 		if (!PG_ARGISNULL(2))
+ 			address = TextDatumGetCString(PG_GETARG_DATUM(2));
+ 
+ 		if (!PG_ARGISNULL(3))
+ 			hostname = TextDatumGetCString(PG_GETARG_DATUM(3));
+ 	}
+ 
+ 	if (load_hba_failure)
+ 		ereport(WARNING,
+ 				(errmsg("There was some failure in reloading pg_hba.conf file. "
+ 				"The pg_hba.conf settings data may contains stale information")));
+ 
+ 	/*
+ 	* Loop through the list and deparse each entry as it comes, storing it in
+ 	* the tuplestore. Any temporary memory allocations here live only for the
+ 	* function call lifetime.
+ 	*/
+ 	foreach(line, parsed_hba_lines)
+ 	{
+ 		HbaLine    *hba = (HbaLine *)lfirst(line);
+ 		Datum		value;
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		if (pg_hba_match(hba, user, database, address, hostname))
+ 		{
+ 			switch (hba->auth_method)
+ 			{
+ 			case uaReject:
+ 				value = CStringGetTextDatum("reject");
+ 				break;
+ 			case uaTrust:
+ 				value = CStringGetTextDatum("trust");
+ 				break;
+ 			case uaIdent:
+ 				value = CStringGetTextDatum("ident");
+ 				break;
+ 			case uaPassword:
+ 				value = CStringGetTextDatum("password");
+ 				break;
+ 			case uaMD5:
+ 				value = CStringGetTextDatum("md5");
+ 				break;
+ 			case uaGSS:
+ 				value = CStringGetTextDatum("gss");
+ 				break;
+ 			case uaSSPI:
+ 				value = CStringGetTextDatum("sspi");
+ 				break;
+ 			case uaPAM:
+ 				value = CStringGetTextDatum("pam");
+ 				break;
+ 			case uaLDAP:
+ 				value = CStringGetTextDatum("ldap");
+ 				break;
+ 			case uaCert:
+ 				value = CStringGetTextDatum("cert");
+ 				break;
+ 			case uaRADIUS:
+ 				value = CStringGetTextDatum("radius");
+ 				break;
+ 			case uaPeer:
+ 				value = CStringGetTextDatum("peer");
+ 				break;
+ 			default:
+ 				elog(ERROR, "unexpected authentication method in parsed HBA entry");
+ 				break;
+ 			}
+ 
+ 			PG_RETURN_TEXT_P(value);
+ 		}
+ 	}
+ 
+ 	PG_RETURN_NULL();
+ }
+ 
+ 
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 4005,4010 **** PostgresMain(int argc, char *argv[],
--- 4005,4023 ----
  		{
  			got_SIGHUP = false;
  			ProcessConfigFile(PGC_SIGHUP);
+ 			
+ 			/* 
+ 			 * Reload authentication config files too to refresh 
+ 			 * pg_hba_conf view data.
+ 			 */
+ 			if (!load_hba())
+ 			{
+ 				ereport(DEBUG1,
+ 					(errmsg("Falure in reloading pg_hba.conf, pg_hba_conf view may show stale information")));
+ 				load_hba_failure = true;
+ 			}
+ 			
+ 			load_hba_failure = false;
  		}
  
  		/*
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3079,3084 **** DATA(insert OID = 2084 (  pg_show_all_settings	PGNSP PGUID 12 1 1000 0 0 f f f f
--- 3079,3088 ----
  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 = 3997 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 2 0 25 "25 25" _null_ _null_ _null_ _null_ _null_ pg_hba_lookup_2args _null_ _null_ _null_));
+ DESCR("view client authentication settings");
+ DATA(insert OID = 3998 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 4 0 25 "25 25 25 25" _null_ _null_ _null_ _null_ _null_ pg_hba_lookup _null_ _null_ _null_));
+ DESCR("view client authentication settings");
  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 = 1065 (  pg_prepared_xact PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{28,25,1184,26,26}" "{o,o,o,o,o}" "{transaction,gid,prepared,ownerid,dbid}" _null_ _null_ pg_prepared_xact _null_ _null_ _null_ ));
*** a/src/include/libpq/hba.h
--- b/src/include/libpq/hba.h
***************
*** 96,101 **** typedef struct IdentLine
--- 96,103 ----
  /* kluge to avoid including libpq/libpq-be.h here */
  typedef struct Port hbaPort;
  
+ extern bool load_hba_failure;
+ 
  extern bool load_hba(void);
  extern bool load_ident(void);
  extern void hba_getauthmethod(hbaPort *port);
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 1122,1127 **** extern Datum show_config_by_name_missing_ok(PG_FUNCTION_ARGS);
--- 1122,1129 ----
  extern Datum set_config_by_name(PG_FUNCTION_ARGS);
  extern Datum show_all_settings(PG_FUNCTION_ARGS);
  extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
+ extern Datum pg_hba_lookup_2args(PG_FUNCTION_ARGS);
+ extern Datum pg_hba_lookup(PG_FUNCTION_ARGS);
  
  /* rls.c */
  extern Datum row_security_active(PG_FUNCTION_ARGS);
#10Pavel Stehule
pavel.stehule@gmail.com
In reply to: Haribabu Kommi (#9)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

2015-11-25 8:05 GMT+01:00 Haribabu Kommi <kommi.haribabu@gmail.com>:

On Tue, Nov 17, 2015 at 9:37 AM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 11/16/15 2:37 AM, Haribabu Kommi wrote:

On Mon, Nov 16, 2015 at 2:30 PM, Peter Eisentraut <peter_e@gmx.net>

wrote:

On 7/21/15 5:15 AM, Haribabu Kommi wrote:

With the output of this view, administrator can identify the lines
that are matching for the given
criteria easily without going through the file.

How is this useful? I could see the use if you want to debug cases of
user foo on host bar says they can't connect, but you can't impersonate
them to verify it. But then all you need is a function with a scalar
result, not a result set.

Do you mean the function should return true or false based on the

connection

status with the provided arguments?

I also feel difficult to understand the function result as compared to

a view.

An hba lookup is essentially a lookup by user name, database name,
client address, yielding an authentication method (possibly with
parameters). So I think this function should work that way as well:
arguments are user name, database name, and so on, and the return value
is an authentication method. Maybe it would be some kind of record,
with line number and some parameters.

That would address the use case I put forth above. I don't know whether
that's what you were going for.

Thanks. Here I attached the poc patch that returns authentication method
of the
first matched hba entry in pg_hba.conf with the given input values.
Currently these
functions returns text type. Based on the details required to be
printed, it can
be changed.

postgres=# select pg_hba_lookup('all', 'all');
pg_hba_lookup
---------------
trust
(1 row)

comments for the approach?

From my perspective, it shows too less informations.

What I am expecting:

1. line num of choosed rule
2. some tracing - via NOTICE, what and why some rules was skipped.

Regards

Pavel

Show quoted text

Regards,
Hari Babu
Fujitsu Australia

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Pavel Stehule (#10)
1 attachment(s)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Wed, Nov 25, 2015 at 7:18 PM, Pavel Stehule <pavel.stehule@gmail.com> wrote:

2015-11-25 8:05 GMT+01:00 Haribabu Kommi <kommi.haribabu@gmail.com>:

Thanks. Here I attached the poc patch that returns authentication method
of the
first matched hba entry in pg_hba.conf with the given input values.
Currently these
functions returns text type. Based on the details required to be
printed, it can
be changed.

postgres=# select pg_hba_lookup('all', 'all');
pg_hba_lookup
---------------
trust
(1 row)

comments for the approach?

From my perspective, it shows too less informations.

What I am expecting:

1. line num of choosed rule
2. some tracing - via NOTICE, what and why some rules was skipped.

Here I attached the patch with the suggested changes.
Along with line number, I kept the options column also with authentication
options as a jsonb datatype.

Example output:

postgres=# select pg_hba_lookup('test','all','::1');
NOTICE: Skipped 84 Hba line, because of non matching IP.
NOTICE: Skipped 86 Hba line, because of non matching database.
NOTICE: Skipped 87 Hba line, because of non matching role.
pg_hba_lookup
---------------
(89,trust,{})
(1 row)

comments?

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_hba_lookup_poc_v3.patchapplication/octet-stream; name=pg_hba_lookup_poc_v3.patchDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 16576,16581 **** SELECT collation for ('foo' COLLATE "de_DE");
--- 16576,16594 ----
         <entry><type>text</type></entry>
         <entry>set parameter and return new value</entry>
        </row>
+ 	  <row>
+        <entry>
+         <indexterm>
+          <primary>pg_hba_lookup</primary>
+         </indexterm>
+         <literal><function>pg_hba_lookup(<parameter>database</> <type>text</>,
+ 							<parameter>user</> <type>text</>
+ 							[, <parameter>address</> <type>text</>]
+ 							[, <parameter>hostname</> <type>text</>)</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>Returns all matching row entries of pg_hba.conf</entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
***************
*** 16633,16638 **** SELECT set_config('log_statement_stats', 'off', false);
--- 16646,16667 ----
  </programlisting>
     </para>
  
+    <para>
+     <function>pg_hba_lookup</function> returns a set of records
+ 	containing the line number, type, database, user, address,
+ 	hostname, method and options that are satisfied with the given
+ 	input values. Typical usages include:
+ <programlisting>
+ SELECT pg_hba_lookup('test_database', 'test_user');
+ 
+ 					pg_hba_lookup
+ --------------------------------------------------------------
+  (84,local,"[""all""]","[""all""]",,,trust,{})
+  (86,local,"[""test_database""]","[""test_user""]",,,trust,{})
+ (1 row)
+ </programlisting>
+    </para>
+    
    </sect2>
  
    <sect2 id="functions-admin-signal">
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 25,39 ****
--- 25,45 ----
  #include <arpa/inet.h>
  #include <unistd.h>
  
+ #include "access/htup_details.h"
  #include "catalog/pg_collation.h"
+ #include "catalog/pg_type.h"
+ #include "funcapi.h"
  #include "libpq/ip.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/jsonb.h"
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  
***************
*** 74,79 **** typedef struct HbaToken
--- 80,95 ----
  	bool		quoted;
  } HbaToken;
  
+ typedef enum hba_lookup_args_mode
+ {
+ 	TWO_ARGS_MODE=0,
+ 	THREE_ARGS_MODE,
+ 	FOUR_ARGS_MODE
+ }hba_lookup_args_mode;
+ 
+ /* Flag to indicate the failure of reloading pg_hba.conf file */
+ bool		load_hba_failure = false;
+ 
  /*
   * pre-parsed content of HBA config file: list of HbaLine structs.
   * parsed_hba_context is the memory context where it lives.
***************
*** 100,105 **** static List *tokenize_inc_file(List *tokens, const char *outer_filename,
--- 116,125 ----
  static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
  				   int line_num);
  
+ static void hba_getvalues_for_line(HbaLine *hba, Datum *values, bool *nulls);
+ static bool pg_hba_match(HbaLine *hba, char *database, char *user,  struct sockaddr_storage *addr,
+ 				char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode);
+ 
  /*
   * isblank() exists in the ISO C99 spec, but it's not very portable yet,
   * so provide our own version.
***************
*** 2233,2235 **** hba_getauthmethod(hbaPort *port)
--- 2253,2826 ----
  {
  	check_hba(port);
  }
+ 
+ 
+ /* LDAP supports 10 currently, keep this well above the most any method needs */
+ #define MAX_OPTIONS 12
+ 
+ /*
+  * Fill in suitable values to build a tuple representing the
+  * HbaLine provided
+  */
+ static void
+ hba_getvalues_for_line(HbaLine *hba, Datum *values, bool *nulls)
+ {
+ 	int			index = 0;
+ 	JsonbInState result;
+ 	JsonbValue	v;
+ 	char	   *str;
+ 	int			len;
+ 
+ 	/* line_number */
+ 	values[index] = Int32GetDatum(hba->linenumber);
+ 
+ 	/* method */
+  	index++;
+  	switch (hba->auth_method)
+  	{
+  		case uaReject:
+  			values[index] = CStringGetTextDatum("reject");
+  			break;
+  		case uaTrust:
+  			values[index] = CStringGetTextDatum("trust");
+  			break;
+  		case uaIdent:
+  			values[index] = CStringGetTextDatum("ident");
+  			break;
+  		case uaPassword:
+  			values[index] = CStringGetTextDatum("password");
+  			break;
+  		case uaMD5:
+  			values[index] = CStringGetTextDatum("md5");
+  			break;
+  		case uaGSS:
+  			values[index] = CStringGetTextDatum("gss");
+  			break;
+  		case uaSSPI:
+  			values[index] = CStringGetTextDatum("sspi");
+  			break;
+  		case uaPAM:
+  			values[index] = CStringGetTextDatum("pam");
+  			break;
+  		case uaLDAP:
+  			values[index] = CStringGetTextDatum("ldap");
+  			break;
+  		case uaCert:
+  			values[index] = CStringGetTextDatum("cert");
+  			break;
+  		case uaRADIUS:
+  			values[index] = CStringGetTextDatum("radius");
+  			break;
+  		case uaPeer:
+  			values[index] = CStringGetTextDatum("peer");
+  			break;
+  		default:
+  			elog(ERROR, "unexpected authentication method in parsed HBA entry");
+  			break;
+  	}
+ 
+ 	/* options */
+ 	index++;
+ 	memset(&result, 0, sizeof(JsonbInState));
+ 	result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
+ 
+ 	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
+ 	{
+ 		if (hba->include_realm)
+ 		{
+ 			str = pstrdup("include_realm");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(BoolGetDatum(true), false, &result, BOOLOID, false);
+ 		}
+ 
+ 		if (hba->krb_realm)
+ 		{
+ 			str = pstrdup("krb_realm");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->krb_realm), false, &result, TEXTOID, false);
+ 		}
+ 	}
+ 
+ 	if (hba->usermap)
+ 	{
+ 		str = pstrdup("map");
+ 		len = strlen(str);
+ 
+ 		v.type = jbvString;
+ 
+ 		v.val.string.len = len;
+ 		v.val.string.val = str;
+ 
+ 		result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 		add_jsonb(CStringGetTextDatum(hba->usermap), false, &result, TEXTOID, false);
+ 	}
+ 
+ 	if (hba->auth_method == uaLDAP)
+ 	{
+ 		if (hba->ldapserver)
+ 		{
+ 			str = pstrdup("ldapserver");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapserver), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapport)
+ 		{
+ 			str = pstrdup("ldapport");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(Int32GetDatum(hba->ldapport), false, &result, INT4OID, false);
+ 		}
+ 
+ 		if (hba->ldaptls)
+ 		{
+ 			str = pstrdup("ldaptls");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(BoolGetDatum(true), false, &result, BOOLOID, false);
+ 		}
+ 
+ 		if (hba->ldapprefix)
+ 		{
+ 			str = pstrdup("ldapprefix");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapprefix), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapsuffix)
+ 		{
+ 			str = pstrdup("ldapsuffix");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapsuffix), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapbasedn)
+ 		{
+ 			str = pstrdup("ldapbasedn");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapbasedn), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapbinddn)
+ 		{
+ 			str = pstrdup("ldapbinddn");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapbinddn), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapbindpasswd)
+ 		{
+ 			str = pstrdup("ldapbindpasswd");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapbindpasswd), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapsearchattribute)
+ 		{
+ 			str = pstrdup("ldapsearchattribute");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapsearchattribute), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapscope)
+ 		{
+ 			str = pstrdup("ldapscope");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(Int32GetDatum(hba->ldapscope), false, &result, INT4OID, false);
+ 		}
+ 	}
+ 
+ 	if (hba->auth_method == uaRADIUS)
+ 	{
+ 		if (hba->radiusserver)
+ 		{
+ 			str = pstrdup("radiusserver");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->radiusserver), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->radiussecret)
+ 		{
+ 			str = pstrdup("radiussecret");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->radiussecret), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->radiusidentifier)
+ 		{
+ 			str = pstrdup("radiusidentifier");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->radiusidentifier), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->radiusport)
+ 		{
+ 			str = pstrdup("radiusport");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(Int32GetDatum(hba->radiusport), false, &result, INT4OID, false);
+ 		}
+ 	}
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
+ 	values[index] = PointerGetDatum(JsonbValueToJsonb(result.res));
+ }
+ 
+ static bool
+ pg_hba_match(HbaLine *hba, char *database, char *user, struct sockaddr_storage *addr,
+ 			char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode)
+ {
+ 	Oid			roleid;
+ 
+ 	if (ssl_inuse)
+ 	{
+ #ifdef USE_SSL
+ 		if (EnableSSL && hba->conntype != ctHostSSL)
+ 		{
+ 			ereport(NOTICE, (errmsg("Skipped %d Hba line, because of non SSL connection type.",
+ 					hba->linenumber)));
+ 			return false;
+ 		}
+ 		else
+ 		{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 					 errmsg("hostssl requires SSL to be turned on"),
+ 					 errhint("Set ssl = on in postgresql.conf.")));
+ 			return false;
+ 		}
+ #else
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 				 errmsg("hostssl is not supported by this build"),
+ 		  errhint("Compile with --with-openssl to use SSL connections.")));
+ 		return false;
+ #endif
+ 	}
+ 
+ 	/* Get the target role's OID.  Note we do not error out for bad role. */
+ 	roleid = get_role_oid(user, true);
+ 
+ 	if (!check_db(database, user, roleid, hba->databases))
+ 	{
+ 		ereport(NOTICE, (errmsg("Skipped %d Hba line, because of non matching database.",
+ 				hba->linenumber)));
+ 		return false;
+ 	}
+ 
+ 	if (!check_role(user, roleid, hba->roles))
+ 	{
+ 		ereport(NOTICE, (errmsg("Skipped %d Hba line, because of non matching role.",
+ 				hba->linenumber)));
+ 		return false;
+ 	}
+ 
+ 	if (args_mode != TWO_ARGS_MODE)
+ 	{
+ 		/* Check IP address */
+ 		switch (hba->ip_cmp_method)
+ 		{
+ 			case ipCmpMask:
+ 				if (hostname && hba->hostname)
+ 				{
+ 					if (!hostname_match(hostname, hba->hostname))
+ 					{
+ 						ereport(NOTICE, (errmsg("Skipped %d Hba line, because of non matching hostname.",
+ 								hba->linenumber)));
+ 						return false;
+ 					}
+ 				}
+ 				else
+ 				{
+ 					if (!check_ip((SockAddr *)addr,
+ 								  (struct sockaddr *) & hba->addr,
+ 								  (struct sockaddr *) & hba->mask))
+ 					{
+ 						ereport(NOTICE, (errmsg("Skipped %d Hba line, because of non matching IP.",
+ 								hba->linenumber)));
+ 						return false;
+ 					}
+ 				}
+ 				break;
+ 			case ipCmpAll:
+ 				break;
+ 			case ipCmpSameHost:
+ 			case ipCmpSameNet:
+ 				if (!check_same_host_or_net((SockAddr *)addr,
+ 											hba->ip_cmp_method))
+ 				{
+ 					ereport(NOTICE, (errmsg("Skipped %d Hba line, because of non matching SameHost/SameNet.",
+ 							hba->linenumber)));
+ 					return false;
+ 				}
+ 				break;
+ 			default: /* Not reachable case*/
+ 				break;
+ 		}
+ 	}
+ 
+ 	return true;
+ }
+ 
+ /*
+  * SQL-accessible SRF to return all the settings from the pg_hba.conf
+  * file.
+  */
+ Datum
+ pg_hba_lookup_2args(PG_FUNCTION_ARGS)
+ {
+ 	return pg_hba_lookup(fcinfo);
+ }
+ 
+ /*
+  * SQL-accessible SRF to return all the settings from the pg_hba.conf
+  * file.
+  */
+ Datum
+ pg_hba_lookup_3args(PG_FUNCTION_ARGS)
+ {
+ 	return pg_hba_lookup(fcinfo);
+ }
+ 
+ #define NUM_PG_HBA_CONF_ATTS   3
+ 
+ /*
+  * SQL-accessible SRF to return all the settings from the pg_hba.conf
+  * file. 
+  */
+ Datum
+ pg_hba_lookup(PG_FUNCTION_ARGS)
+ {
+ 	char	*user;
+ 	char	*database;
+ 	char	*address = NULL;
+ 	bool    ssl_inuse = false;
+ 	TupleDesc	tupdesc;
+ 	ListCell   *line;
+ 	Datum		values[NUM_PG_HBA_CONF_ATTS];
+ 	bool		nulls[NUM_PG_HBA_CONF_ATTS];
+ 	HeapTuple	tuple;
+ 	Datum		result;
+ 	struct sockaddr_storage addr;
+ 	char *hostname = NULL;
+ 	hba_lookup_args_mode args_mode = TWO_ARGS_MODE; /* Minimum number of arguments */
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("only superuser can view pg_hba.conf settings"))));
+ 
+ 	if (PG_ARGISNULL(0))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 				(errmsg("database name is required to match pg_hba configuration entry"))));
+ 	else
+ 		database = TextDatumGetCString(PG_GETARG_DATUM(0));
+ 
+ 	if (PG_ARGISNULL(1))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 				(errmsg("user name is required to match pg_hba configuration entry"))));
+ 	else
+ 		user = TextDatumGetCString(PG_GETARG_DATUM(1));
+ 
+ 	if (PG_NARGS() != 2)
+ 	{
+ 		struct addrinfo *gai_result;
+ 		struct addrinfo hints;
+ 		int ret;
+ 
+ 		args_mode = THREE_ARGS_MODE;
+ 		if (!PG_ARGISNULL(2))
+ 			address = TextDatumGetCString(PG_GETARG_DATUM(2));
+ 
+ 		/* Get the IP address either way */
+ 		hints.ai_flags = AI_NUMERICHOST;
+ 		hints.ai_family = AF_UNSPEC;
+ 		hints.ai_socktype = 0;
+ 		hints.ai_protocol = 0;
+ 		hints.ai_addrlen = 0;
+ 		hints.ai_canonname = NULL;
+ 		hints.ai_addr = NULL;
+ 		hints.ai_next = NULL;
+ 
+ 		ret = pg_getaddrinfo_all(address, NULL, &hints, &gai_result);
+ 		if (ret == 0 && gai_result)
+ 			memcpy(&addr, gai_result->ai_addr, gai_result->ai_addrlen);
+ 		else if (ret == EAI_NONAME)
+ 			hostname = pstrdup(address);
+ 		else
+ 		{
+ 			ereport(ERROR,
+ 					(errmsg("invalid IP address \"%s\": %s",
+ 							 address, gai_strerror(ret))));
+ 			if (gai_result)
+ 				pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ 			return false;
+ 		}
+ 
+ 		pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ 
+ 		if (PG_NARGS() != 3)
+ 		{
+ 			args_mode = FOUR_ARGS_MODE;
+ 			if (!PG_ARGISNULL(3))
+ 				ssl_inuse = DatumGetBool(PG_GETARG_DATUM(3));
+ 		}
+ 	}
+ 
+ 	if (load_hba_failure)
+ 		ereport(WARNING,
+ 				(errmsg("There was some failure in reloading pg_hba.conf file. "
+ 				"The pg_hba.conf settings data may contains stale information")));
+ 
+ 	/* Create the tupledesc	*/
+ 	tupdesc = CreateTemplateTupleDesc(NUM_PG_HBA_CONF_ATTS, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)1, "line_number",
+ 		INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)2, "method",
+ 		TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)3, "options",
+ 		JSONBOID, -1, 0);
+ 	BlessTupleDesc(tupdesc);
+ 
+ 	MemSet(values, 0, sizeof(values));
+ 	MemSet(nulls, true, sizeof(nulls));
+ 
+ 	/*
+ 	 * Loop through the list and deparse each entry as it comes, and returns
+ 	 * the first matched entry.
+ 	 */
+ 	foreach(line, parsed_hba_lines)
+ 	{
+ 		HbaLine    *hba = (HbaLine *)lfirst(line);
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		if (pg_hba_match(hba, database, user, &addr, hostname, ssl_inuse, args_mode))
+ 		{
+ 			/* Get the next parsed hba line values */
+ 			MemSet(nulls, false, sizeof(nulls));
+ 			hba_getvalues_for_line(hba, values, nulls);
+ 			break;
+ 		}
+ 	}
+ 
+ 	/* build a tuple */
+ 	tuple = heap_form_tuple(tupdesc, values, nulls);
+ 
+ 	/* make the tuple into a datum */
+ 	result = HeapTupleGetDatum(tuple);
+ 	PG_RETURN_DATUM(result);
+ }
+ 
+ 
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 4005,4010 **** PostgresMain(int argc, char *argv[],
--- 4005,4023 ----
  		{
  			got_SIGHUP = false;
  			ProcessConfigFile(PGC_SIGHUP);
+ 			
+ 			/* 
+ 			 * Reload authentication config files too to refresh 
+ 			 * pg_hba_conf view data.
+ 			 */
+ 			if (!load_hba())
+ 			{
+ 				ereport(DEBUG1,
+ 					(errmsg("Falure in reloading pg_hba.conf, pg_hba_conf view may show stale information")));
+ 				load_hba_failure = true;
+ 			}
+ 			
+ 			load_hba_failure = false;
  		}
  
  		/*
*** a/src/backend/utils/adt/jsonb.c
--- b/src/backend/utils/adt/jsonb.c
***************
*** 28,39 ****
  #include "utils/syscache.h"
  #include "utils/typcache.h"
  
- typedef struct JsonbInState
- {
- 	JsonbParseState *parseState;
- 	JsonbValue *res;
- } JsonbInState;
- 
  /* unlike with json categories, we need to treat json and jsonb differently */
  typedef enum					/* type categories for datum_to_jsonb */
  {
--- 28,33 ----
***************
*** 83,89 **** static void jsonb_categorize_type(Oid typoid,
  static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
  			   JsonbTypeCategory tcategory, Oid outfuncoid,
  			   bool key_scalar);
! static void add_jsonb(Datum val, bool is_null, JsonbInState *result,
  		  Oid val_type, bool key_scalar);
  static JsonbParseState *clone_parse_state(JsonbParseState *state);
  static char *JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent);
--- 77,83 ----
  static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
  			   JsonbTypeCategory tcategory, Oid outfuncoid,
  			   bool key_scalar);
! void add_jsonb(Datum val, bool is_null, JsonbInState *result,
  		  Oid val_type, bool key_scalar);
  static JsonbParseState *clone_parse_state(JsonbParseState *state);
  static char *JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent);
***************
*** 1113,1119 **** composite_to_jsonb(Datum composite, JsonbInState *result)
   * lookups only once.
   */
  
! static void
  add_jsonb(Datum val, bool is_null, JsonbInState *result,
  		  Oid val_type, bool key_scalar)
  {
--- 1107,1113 ----
   * lookups only once.
   */
  
! void
  add_jsonb(Datum val, bool is_null, JsonbInState *result,
  		  Oid val_type, bool key_scalar)
  {
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3079,3084 **** DATA(insert OID = 2084 (  pg_show_all_settings	PGNSP PGUID 12 1 1000 0 0 f f f f
--- 3079,3090 ----
  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 = 3997 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v u 2 0 2249 "25 25" "{25,25,23,25,3802}" "{i,i,o,o,o}" "{database,user,line_number,method,options}" _null_ _null_ pg_hba_lookup_2args _null_ _null_ _null_));
+ DESCR("view client authentication settings");
+ DATA(insert OID = 3998 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v u 3 0 2249 "25 25 25" "{25,25,25,23,25,3802}" "{i,i,i,o,o,o}" "{database,user,address,line_number,method,options}" _null_ _null_ pg_hba_lookup_3args _null_ _null_ _null_));
+ DESCR("view client authentication settings");
+ DATA(insert OID = 3999 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v u 4 0 2249 "25 25 25 16" "{25,25,25,16,23,25,3802}" "{i,i,i,i,o,o,o}" "{database,user,address,ssl_inuse,line_number,method,options}" _null_ _null_ pg_hba_lookup _null_ _null_ _null_));
+ DESCR("view client authentication settings");
  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 = 1065 (  pg_prepared_xact PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{28,25,1184,26,26}" "{o,o,o,o,o}" "{transaction,gid,prepared,ownerid,dbid}" _null_ _null_ pg_prepared_xact _null_ _null_ _null_ ));
*** a/src/include/libpq/hba.h
--- b/src/include/libpq/hba.h
***************
*** 96,101 **** typedef struct IdentLine
--- 96,103 ----
  /* kluge to avoid including libpq/libpq-be.h here */
  typedef struct Port hbaPort;
  
+ extern bool load_hba_failure;
+ 
  extern bool load_hba(void);
  extern bool load_ident(void);
  extern void hba_getauthmethod(hbaPort *port);
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 1123,1128 **** extern Datum set_config_by_name(PG_FUNCTION_ARGS);
--- 1123,1133 ----
  extern Datum show_all_settings(PG_FUNCTION_ARGS);
  extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
  
+ /* hba.c */
+ extern Datum pg_hba_lookup_2args(PG_FUNCTION_ARGS);
+ extern Datum pg_hba_lookup_3args(PG_FUNCTION_ARGS);
+ extern Datum pg_hba_lookup(PG_FUNCTION_ARGS);
+ 
  /* rls.c */
  extern Datum row_security_active(PG_FUNCTION_ARGS);
  extern Datum row_security_active_name(PG_FUNCTION_ARGS);
*** a/src/include/utils/json.h
--- b/src/include/utils/json.h
***************
*** 43,49 **** extern Datum json_object(PG_FUNCTION_ARGS);
  extern Datum json_object_two_arg(PG_FUNCTION_ARGS);
  
  extern void escape_json(StringInfo buf, const char *str);
- 
  extern Datum json_typeof(PG_FUNCTION_ARGS);
  
  /* functions in jsonfuncs.c */
--- 43,48 ----
*** a/src/include/utils/jsonb.h
--- b/src/include/utils/jsonb.h
***************
*** 343,348 **** typedef struct JsonbIterator
--- 343,354 ----
  	struct JsonbIterator *parent;
  } JsonbIterator;
  
+ typedef struct JsonbInState
+ {
+ 	JsonbParseState *parseState;
+ 	JsonbValue *res;
+ } JsonbInState;
+ 
  /* I/O routines */
  extern Datum jsonb_in(PG_FUNCTION_ARGS);
  extern Datum jsonb_out(PG_FUNCTION_ARGS);
***************
*** 428,433 **** extern bool JsonbDeepContains(JsonbIterator **val,
--- 434,441 ----
  extern void JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash);
  
  /* jsonb.c support functions */
+ void add_jsonb(Datum val, bool is_null, JsonbInState *result,
+ 	Oid val_type, bool key_scalar);
  extern char *JsonbToCString(StringInfo out, JsonbContainer *in,
  			   int estimated_len);
  extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in,
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
***************
*** 1327,1332 **** pg_group| SELECT pg_authid.rolname AS groname,
--- 1327,1344 ----
            WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist
     FROM pg_authid
    WHERE (NOT pg_authid.rolcanlogin);
+ pg_hba_conf| SELECT a.line_number,
+     a.type,
+     a.keyword_databases,
+     a.databases,
+     a.keyword_users,
+     a.users,
+     a.address,
+     a.compare_method,
+     a.hostname,
+     a.method,
+     a.options
+    FROM pg_hba_conf() a(line_number, type, keyword_databases, databases, keyword_users, users, address, compare_method, hostname, method, options);
  pg_indexes| SELECT n.nspname AS schemaname,
      c.relname AS tablename,
      i.relname AS indexname,
#12Pavel Stehule
pavel.stehule@gmail.com
In reply to: Haribabu Kommi (#11)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

2015-12-03 5:00 GMT+01:00 Haribabu Kommi <kommi.haribabu@gmail.com>:

On Wed, Nov 25, 2015 at 7:18 PM, Pavel Stehule <pavel.stehule@gmail.com>
wrote:

2015-11-25 8:05 GMT+01:00 Haribabu Kommi <kommi.haribabu@gmail.com>:

Thanks. Here I attached the poc patch that returns authentication method
of the
first matched hba entry in pg_hba.conf with the given input values.
Currently these
functions returns text type. Based on the details required to be
printed, it can
be changed.

postgres=# select pg_hba_lookup('all', 'all');
pg_hba_lookup
---------------
trust
(1 row)

comments for the approach?

From my perspective, it shows too less informations.

What I am expecting:

1. line num of choosed rule
2. some tracing - via NOTICE, what and why some rules was skipped.

Here I attached the patch with the suggested changes.
Along with line number, I kept the options column also with authentication
options as a jsonb datatype.

Example output:

postgres=# select pg_hba_lookup('test','all','::1');
NOTICE: Skipped 84 Hba line, because of non matching IP.
NOTICE: Skipped 86 Hba line, because of non matching database.
NOTICE: Skipped 87 Hba line, because of non matching role.
pg_hba_lookup
---------------
(89,trust,{})
(1 row)

comments?

I liked it

The text of notice can be reduced "Skipped xx line, ..." - it have to be
pg_hba

Pavel

Show quoted text

Regards,
Hari Babu
Fujitsu Australia

#13Pavel Stehule
pavel.stehule@gmail.com
In reply to: Pavel Stehule (#12)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

2015-12-03 5:53 GMT+01:00 Pavel Stehule <pavel.stehule@gmail.com>:

2015-12-03 5:00 GMT+01:00 Haribabu Kommi <kommi.haribabu@gmail.com>:

On Wed, Nov 25, 2015 at 7:18 PM, Pavel Stehule <pavel.stehule@gmail.com>
wrote:

2015-11-25 8:05 GMT+01:00 Haribabu Kommi <kommi.haribabu@gmail.com>:

Thanks. Here I attached the poc patch that returns authentication

method

of the
first matched hba entry in pg_hba.conf with the given input values.
Currently these
functions returns text type. Based on the details required to be
printed, it can
be changed.

postgres=# select pg_hba_lookup('all', 'all');
pg_hba_lookup
---------------
trust
(1 row)

comments for the approach?

From my perspective, it shows too less informations.

What I am expecting:

1. line num of choosed rule
2. some tracing - via NOTICE, what and why some rules was skipped.

Here I attached the patch with the suggested changes.
Along with line number, I kept the options column also with authentication
options as a jsonb datatype.

Example output:

postgres=# select pg_hba_lookup('test','all','::1');
NOTICE: Skipped 84 Hba line, because of non matching IP.
NOTICE: Skipped 86 Hba line, because of non matching database.
NOTICE: Skipped 87 Hba line, because of non matching role.
pg_hba_lookup
---------------
(89,trust,{})
(1 row)

comments?

I liked it

The text of notice can be reduced "Skipped xx line, ..." - it have to be
pg_hba

this tracing can be implemented to main pg_hba processing. When you are
connect from some specific client - and you can see, why you cannot to
connect to Postgres

Pavel

Show quoted text

Pavel

Regards,
Hari Babu
Fujitsu Australia

#14Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Pavel Stehule (#13)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

Here I attached the patch with the suggested changes.
Along with line number, I kept the options column also with authentication
options as a jsonb datatype.

Example output:

postgres=# select pg_hba_lookup('test','all','::1');
NOTICE: Skipped 84 Hba line, because of non matching IP.
NOTICE: Skipped 86 Hba line, because of non matching database.
NOTICE: Skipped 87 Hba line, because of non matching role.
pg_hba_lookup
---------------
(89,trust,{})
(1 row)

comments?

I don't like this interface. It's nice for psql, but everybody else is
going to lose. I think these should be reported in the SRF result set
as well; perhaps add a "mode" column that says "skipped" for such rows,
and "matched" for the one that, uh, matches. (Please try calling your
function with "select * from" which should give nicer output.)

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Alvaro Herrera (#14)
1 attachment(s)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Fri, Dec 4, 2015 at 8:05 AM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:

Here I attached the patch with the suggested changes.
Along with line number, I kept the options column also with authentication
options as a jsonb datatype.

Example output:

postgres=# select pg_hba_lookup('test','all','::1');
NOTICE: Skipped 84 Hba line, because of non matching IP.
NOTICE: Skipped 86 Hba line, because of non matching database.
NOTICE: Skipped 87 Hba line, because of non matching role.
pg_hba_lookup
---------------
(89,trust,{})
(1 row)

comments?

I don't like this interface. It's nice for psql, but everybody else is
going to lose. I think these should be reported in the SRF result set
as well; perhaps add a "mode" column that says "skipped" for such rows,
and "matched" for the one that, uh, matches. (Please try calling your
function with "select * from" which should give nicer output.)

How about as follows?

postgres=# select * from pg_hba_lookup('all','all','::1');
line_number | type | database | user | address | hostname |
method | options | mode
-------------+-------+----------+---------+-----------+----------+--------+---------+---------
84 | local | ["all"] | ["all"] |
| | trust | {} | skipped
86 | host | ["all"] | ["all"] | 127.0.0.1 |
| trust | {} | skipped
88 | host | ["all"] | ["all"] | ::1
| | trust | {} | matched
(3 rows)

In the above case, all the columns are displayed. Based on the
feedback we can keep
the required columns. I didn't yet removed the NOTICE messages in the
attached version.
Are they still required?

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_hba_lookup_poc_v4.patchapplication/octet-stream; name=pg_hba_lookup_poc_v4.patchDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 16576,16581 **** SELECT collation for ('foo' COLLATE "de_DE");
--- 16576,16594 ----
         <entry><type>text</type></entry>
         <entry>set parameter and return new value</entry>
        </row>
+ 	  <row>
+        <entry>
+         <indexterm>
+          <primary>pg_hba_lookup</primary>
+         </indexterm>
+         <literal><function>pg_hba_lookup(<parameter>database</> <type>text</>,
+ 							<parameter>user</> <type>text</>
+ 							[, <parameter>address</> <type>text</>]
+ 							[, <parameter>hostname</> <type>text</>)</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>Returns all matching row entries of pg_hba.conf</entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
***************
*** 16633,16638 **** SELECT set_config('log_statement_stats', 'off', false);
--- 16646,16667 ----
  </programlisting>
     </para>
  
+    <para>
+     <function>pg_hba_lookup</function> returns a set of records
+ 	containing the line number, type, database, user, address,
+ 	hostname, method and options that are satisfied with the given
+ 	input values. Typical usages include:
+ <programlisting>
+ SELECT pg_hba_lookup('test_database', 'test_user');
+ 
+ 					pg_hba_lookup
+ --------------------------------------------------------------
+  (84,local,"[""all""]","[""all""]",,,trust,{})
+  (86,local,"[""test_database""]","[""test_user""]",,,trust,{})
+ (1 row)
+ </programlisting>
+    </para>
+    
    </sect2>
  
    <sect2 id="functions-admin-signal">
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 25,39 ****
--- 25,45 ----
  #include <arpa/inet.h>
  #include <unistd.h>
  
+ #include "access/htup_details.h"
  #include "catalog/pg_collation.h"
+ #include "catalog/pg_type.h"
+ #include "funcapi.h"
  #include "libpq/ip.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/jsonb.h"
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  
***************
*** 74,79 **** typedef struct HbaToken
--- 80,95 ----
  	bool		quoted;
  } HbaToken;
  
+ typedef enum hba_lookup_args_mode
+ {
+ 	TWO_ARGS_MODE=0,
+ 	THREE_ARGS_MODE,
+ 	FOUR_ARGS_MODE
+ }hba_lookup_args_mode;
+ 
+ /* Flag to indicate the failure of reloading pg_hba.conf file */
+ bool		load_hba_failure = false;
+ 
  /*
   * pre-parsed content of HBA config file: list of HbaLine structs.
   * parsed_hba_context is the memory context where it lives.
***************
*** 100,105 **** static List *tokenize_inc_file(List *tokens, const char *outer_filename,
--- 116,128 ----
  static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
  				   int line_num);
  
+ static void hba_getvalues_for_line(HbaLine *hba, Datum *values, bool *nulls,
+ 			bool hba_match_result);
+ static bool pg_hba_match(HbaLine *hba, char *user, char *database, struct sockaddr_storage *addr,
+ 		 				char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode);
+ static void pg_hba_lookup_internal(char	*database, char	*user, struct sockaddr_storage *addr,
+ 			char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode, ReturnSetInfo *rsi);
+ 
  /*
   * isblank() exists in the ISO C99 spec, but it's not very portable yet,
   * so provide our own version.
***************
*** 2233,2235 **** hba_getauthmethod(hbaPort *port)
--- 2256,2969 ----
  {
  	check_hba(port);
  }
+ 
+ 
+ /* LDAP supports 10 currently, keep this well above the most any method needs */
+ #define MAX_OPTIONS 12
+ 
+ /*
+  * Fill in suitable values to build a tuple representing the
+  * HbaLine provided
+  */
+ static void
+ hba_getvalues_for_line(HbaLine *hba, Datum *values, bool *nulls, bool hba_match_result)
+ {
+ 	ListCell   *dbcell;
+ 	char		buffer[NI_MAXHOST];
+ 	int			index = 0;
+ 	JsonbInState result;
+ 	JsonbValue	v;
+ 	char	   *str;
+ 	int			len;
+ 
+ 	/* line_number */
+ 	values[index] = Int32GetDatum(hba->linenumber);
+ 
+ 	/* 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;
+  		default:
+  			elog(ERROR, "unexpected connection type in parsed HBA entry");
+  			break;
+  	}
+ 
+ 	/* database */
+ 	index++;
+ 	memset(&result, 0, sizeof(JsonbInState));
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
+ 
+ 	if (list_length(hba->databases) != 0)
+ 	{
+ 		HbaToken   *tok;
+ 
+ 		foreach(dbcell, hba->databases)
+ 		{
+ 			tok = lfirst(dbcell);
+ 			add_jsonb(CStringGetTextDatum(tok->string), false, &result, TEXTOID, false);
+ 		}
+ 	}
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
+ 	values[index] = PointerGetDatum(JsonbValueToJsonb(result.res));
+ 
+ 	/* user */
+ 	index++;
+ 	memset(&result, 0, sizeof(JsonbInState));
+ 	result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
+ 
+ 	if (list_length(hba->roles) != 0)
+ 	{
+ 		HbaToken   *tok;
+ 
+ 		foreach(dbcell, hba->roles)
+ 		{
+ 			tok = lfirst(dbcell);
+ 			add_jsonb(CStringGetTextDatum(tok->string), false, &result, TEXTOID, false);
+ 		}
+ 	}
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
+ 	values[index] = PointerGetDatum(JsonbValueToJsonb(result.res));
+ 
+ 	/* address */
+ 	index++;
+ 	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+ 	}
+ 	else
+ 		nulls[index] = true;
+ 
+ 	/* hostname */
+ 	index++;
+ 	if (hba->hostname)
+ 		values[index] = CStringGetTextDatum(hba->hostname);
+ 	else
+ 		nulls[index] = true;
+ 
+ 	/* method */
+  	index++;
+  	switch (hba->auth_method)
+  	{
+  		case uaReject:
+  			values[index] = CStringGetTextDatum("reject");
+  			break;
+  		case uaTrust:
+  			values[index] = CStringGetTextDatum("trust");
+  			break;
+  		case uaIdent:
+  			values[index] = CStringGetTextDatum("ident");
+  			break;
+  		case uaPassword:
+  			values[index] = CStringGetTextDatum("password");
+  			break;
+  		case uaMD5:
+  			values[index] = CStringGetTextDatum("md5");
+  			break;
+  		case uaGSS:
+  			values[index] = CStringGetTextDatum("gss");
+  			break;
+  		case uaSSPI:
+  			values[index] = CStringGetTextDatum("sspi");
+  			break;
+  		case uaPAM:
+  			values[index] = CStringGetTextDatum("pam");
+  			break;
+  		case uaLDAP:
+  			values[index] = CStringGetTextDatum("ldap");
+  			break;
+  		case uaCert:
+  			values[index] = CStringGetTextDatum("cert");
+  			break;
+  		case uaRADIUS:
+  			values[index] = CStringGetTextDatum("radius");
+  			break;
+  		case uaPeer:
+  			values[index] = CStringGetTextDatum("peer");
+  			break;
+  		default:
+  			elog(ERROR, "unexpected authentication method in parsed HBA entry");
+  			break;
+  	}
+ 
+ 	/* options */
+ 	index++;
+ 	memset(&result, 0, sizeof(JsonbInState));
+ 	result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
+ 
+ 	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
+ 	{
+ 		if (hba->include_realm)
+ 		{
+ 			str = pstrdup("include_realm");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(BoolGetDatum(true), false, &result, BOOLOID, false);
+ 		}
+ 
+ 		if (hba->krb_realm)
+ 		{
+ 			str = pstrdup("krb_realm");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->krb_realm), false, &result, TEXTOID, false);
+ 		}
+ 	}
+ 
+ 	if (hba->usermap)
+ 	{
+ 		str = pstrdup("map");
+ 		len = strlen(str);
+ 
+ 		v.type = jbvString;
+ 
+ 		v.val.string.len = len;
+ 		v.val.string.val = str;
+ 
+ 		result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 		add_jsonb(CStringGetTextDatum(hba->usermap), false, &result, TEXTOID, false);
+ 	}
+ 
+ 	if (hba->auth_method == uaLDAP)
+ 	{
+ 		if (hba->ldapserver)
+ 		{
+ 			str = pstrdup("ldapserver");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapserver), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapport)
+ 		{
+ 			str = pstrdup("ldapport");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(Int32GetDatum(hba->ldapport), false, &result, INT4OID, false);
+ 		}
+ 
+ 		if (hba->ldaptls)
+ 		{
+ 			str = pstrdup("ldaptls");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(BoolGetDatum(true), false, &result, BOOLOID, false);
+ 		}
+ 
+ 		if (hba->ldapprefix)
+ 		{
+ 			str = pstrdup("ldapprefix");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapprefix), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapsuffix)
+ 		{
+ 			str = pstrdup("ldapsuffix");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapsuffix), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapbasedn)
+ 		{
+ 			str = pstrdup("ldapbasedn");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapbasedn), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapbinddn)
+ 		{
+ 			str = pstrdup("ldapbinddn");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapbinddn), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapbindpasswd)
+ 		{
+ 			str = pstrdup("ldapbindpasswd");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapbindpasswd), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapsearchattribute)
+ 		{
+ 			str = pstrdup("ldapsearchattribute");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->ldapsearchattribute), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->ldapscope)
+ 		{
+ 			str = pstrdup("ldapscope");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(Int32GetDatum(hba->ldapscope), false, &result, INT4OID, false);
+ 		}
+ 	}
+ 
+ 	if (hba->auth_method == uaRADIUS)
+ 	{
+ 		if (hba->radiusserver)
+ 		{
+ 			str = pstrdup("radiusserver");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->radiusserver), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->radiussecret)
+ 		{
+ 			str = pstrdup("radiussecret");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->radiussecret), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->radiusidentifier)
+ 		{
+ 			str = pstrdup("radiusidentifier");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(CStringGetTextDatum(hba->radiusidentifier), false, &result, TEXTOID, false);
+ 		}
+ 
+ 		if (hba->radiusport)
+ 		{
+ 			str = pstrdup("radiusport");
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 
+ 			result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 			add_jsonb(Int32GetDatum(hba->radiusport), false, &result, INT4OID, false);
+ 		}
+ 	}
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
+ 	values[index] = PointerGetDatum(JsonbValueToJsonb(result.res));
+ 
+ 	/* mode */
+ 	index++;
+ 	if (hba_match_result)
+ 		values[index] = CStringGetTextDatum("matched");
+ 	else
+ 		values[index] = CStringGetTextDatum("skipped");
+ }
+ 
+ static bool
+ pg_hba_match(HbaLine *hba, char *database, char *user, struct sockaddr_storage *addr,
+ 			char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode)
+ {
+ 	Oid			roleid;
+ 
+ 	if (ssl_inuse)
+ 	{
+ #ifdef USE_SSL
+ 		if (EnableSSL && hba->conntype != ctHostSSL)
+ 		{
+ 			ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of non SSL connection type.",
+ 					hba->linenumber)));
+ 			return false;
+ 		}
+ 		else
+ 		{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 					 errmsg("hostssl requires SSL to be turned on"),
+ 					 errhint("Set ssl = on in postgresql.conf.")));
+ 			return false;
+ 		}
+ #else
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 				 errmsg("hostssl is not supported by this build"),
+ 		  errhint("Compile with --with-openssl to use SSL connections.")));
+ 		return false;
+ #endif
+ 	}
+ 
+ 	/* Get the target role's OID.  Note we do not error out for bad role. */
+ 	roleid = get_role_oid(user, true);
+ 
+ 	if (!check_db(database, user, roleid, hba->databases))
+ 	{
+ 		ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of non matching database.",
+ 				hba->linenumber)));
+ 		return false;
+ 	}
+ 
+ 	if (!check_role(user, roleid, hba->roles))
+ 	{
+ 		ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of non matching role.",
+ 				hba->linenumber)));
+ 		return false;
+ 	}
+ 
+ 	if (args_mode != TWO_ARGS_MODE)
+ 	{
+ 		/* Check IP address */
+ 		switch (hba->ip_cmp_method)
+ 		{
+ 			case ipCmpMask:
+ 				if (hostname && hba->hostname)
+ 				{
+ 					if (!hostname_match(hostname, hba->hostname))
+ 					{
+ 						ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of non matching hostname.",
+ 								hba->linenumber)));
+ 						return false;
+ 					}
+ 				}
+ 				else
+ 				{
+ 					if (!check_ip((SockAddr *)addr,
+ 								  (struct sockaddr *) & hba->addr,
+ 								  (struct sockaddr *) & hba->mask))
+ 					{
+ 						ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of non matching IP.",
+ 								hba->linenumber)));
+ 						return false;
+ 					}
+ 				}
+ 				break;
+ 			case ipCmpAll:
+ 				break;
+ 			case ipCmpSameHost:
+ 			case ipCmpSameNet:
+ 				if (!check_same_host_or_net((SockAddr *)addr,
+ 											hba->ip_cmp_method))
+ 				{
+ 					ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of non matching SameHost/SameNet.",
+ 							hba->linenumber)));
+ 					return false;
+ 				}
+ 				break;
+ 			default: /* Not reachable case*/
+ 				break;
+ 		}
+ 	}
+ 
+ 	return true;
+ }
+ 
+ 
+ #define NUM_PG_HBA_CONF_ATTS   9
+ 
+ static void
+ pg_hba_lookup_internal(char *database, char *user, struct sockaddr_storage *addr,
+ 		char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode, ReturnSetInfo *rsi)
+ {
+ 	Tuplestorestate *tuple_store;
+ 	TupleDesc	tupdesc;
+ 	ListCell   *line;
+ 	MemoryContext old_cxt;
+ 
+ 	/*
+ 	* Create the tupledesc and tuplestore in the per_query context as
+ 	* required for SFRM_Materialize.
+ 	*/
+ 	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+ 
+ 	tupdesc = CreateTemplateTupleDesc(NUM_PG_HBA_CONF_ATTS, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)1, "line_number",
+ 		INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)2, "type",
+ 		TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)3, "database",
+ 		JSONBOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)4, "user",
+ 		JSONBOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)5, "address",
+ 		INETOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)6, "hostname",
+ 		TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)7, "method",
+ 		TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)8, "options",
+ 		JSONBOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)9, "mode",
+ 			TEXTOID, -1, 0);
+ 	BlessTupleDesc(tupdesc);
+ 
+ 	tuple_store =
+ 		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+ 		false, work_mem);
+ 
+ 	MemoryContextSwitchTo(old_cxt);
+ 
+ 	/*
+ 	* Loop through the list and deparse each entry as it comes, storing it in
+ 	* the tuplestore. Any temporary memory allocations here live only for the
+ 	* function call lifetime.
+ 	*/
+ 	foreach(line, parsed_hba_lines)
+ 	{
+ 		HbaLine    *hba = (HbaLine *)lfirst(line);
+ 		Datum		values[NUM_PG_HBA_CONF_ATTS];
+ 		bool		nulls[NUM_PG_HBA_CONF_ATTS];
+ 		HeapTuple	tuple;
+ 		bool hba_match_result;
+ 
+ 		MemSet(values, 0, sizeof(values));
+ 		MemSet(nulls, 0, sizeof(nulls));
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		hba_match_result = pg_hba_match(hba, database, user, addr, hostname, ssl_inuse, args_mode);
+ 
+ 		/* Get the next parsed hba line values */
+ 		hba_getvalues_for_line(hba, values, nulls, hba_match_result);
+ 
+ 		/* build a tuple */
+ 		tuple = heap_form_tuple(tupdesc, values, nulls);
+ 		tuplestore_puttuple(tuple_store, tuple);
+ 	}
+ 
+ 	rsi->setDesc = tupdesc;
+ 	rsi->setResult = tuple_store;
+ 	return;
+ }
+ 
+ /*
+ * SQL-accessible SRF to return all the settings from the pg_hba.conf
+ * file.
+ */
+ Datum
+ pg_hba_lookup_2args(PG_FUNCTION_ARGS)
+ {
+ 	return pg_hba_lookup(fcinfo);
+ }
+ 
+ /*
+  * SQL-accessible SRF to return all the settings from the pg_hba.conf
+  * file.
+  */
+ Datum
+ pg_hba_lookup_3args(PG_FUNCTION_ARGS)
+ {
+ 	return pg_hba_lookup(fcinfo);
+ }
+ 
+ /*
+  * SQL-accessible SRF to return all the settings from the pg_hba.conf
+  * file. 
+  */
+ Datum
+ pg_hba_lookup(PG_FUNCTION_ARGS)
+ {
+ 	char	*user;
+ 	char	*database;
+ 	char	*address;
+ 	char    *hostname;
+ 	bool	ssl_inuse = false;
+ 	struct sockaddr_storage addr;
+ 	hba_lookup_args_mode args_mode = TWO_ARGS_MODE; /* Minimum number of arguments */
+ 
+ 	/*
+ 	* 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.
+ 	*/
+ 	ReturnSetInfo *rsi = (ReturnSetInfo *)fcinfo->resultinfo;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("only superuser can view pg_hba.conf settings"))));
+ 
+ 	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+ 		(rsi->allowedModes & SFRM_Materialize) == 0)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				errmsg("set-valued function called in context that "
+ 						"cannot accept a set")));
+ 
+ 	if (PG_ARGISNULL(0))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 				(errmsg("database name is required to match pg_hba configuration entry"))));
+ 	else
+ 		database = TextDatumGetCString(PG_GETARG_DATUM(0));
+ 
+ 	if (PG_ARGISNULL(1))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 				(errmsg("user name is required to match pg_hba configuration entry"))));
+ 	else
+ 		user = TextDatumGetCString(PG_GETARG_DATUM(1));
+ 
+ 	if (PG_NARGS() != 2)
+ 	{
+ 		struct addrinfo *gai_result;
+ 		struct addrinfo hints;
+ 		int ret;
+ 
+ 		args_mode = THREE_ARGS_MODE;
+ 		if (!PG_ARGISNULL(2))
+ 			address = TextDatumGetCString(PG_GETARG_DATUM(2));
+ 
+ 		/* Get the IP address either way */
+ 		hints.ai_flags = AI_NUMERICHOST;
+ 		hints.ai_family = AF_UNSPEC;
+ 		hints.ai_socktype = 0;
+ 		hints.ai_protocol = 0;
+ 		hints.ai_addrlen = 0;
+ 		hints.ai_canonname = NULL;
+ 		hints.ai_addr = NULL;
+ 		hints.ai_next = NULL;
+ 
+ 		ret = pg_getaddrinfo_all(address, NULL, &hints, &gai_result);
+ 		if (ret == 0 && gai_result)
+ 			memcpy(&addr, gai_result->ai_addr, gai_result->ai_addrlen);
+ 		else if (ret == EAI_NONAME)
+ 			hostname = pstrdup(address);
+ 		else
+ 		{
+ 			ereport(ERROR,
+ 					(errmsg("invalid IP address \"%s\": %s",
+ 							 address, gai_strerror(ret))));
+ 			if (gai_result)
+ 				pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ 			return false;
+ 		}
+ 
+ 		pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ 
+ 		if (PG_NARGS() != 3)
+ 		{
+ 			args_mode = FOUR_ARGS_MODE;
+ 			if (!PG_ARGISNULL(3))
+ 				ssl_inuse = DatumGetBool(PG_GETARG_DATUM(3));
+ 		}
+ 	}
+ 
+ 	if (load_hba_failure)
+ 		ereport(WARNING,
+ 		(errmsg("There was some failure in reloading pg_hba.conf file. "
+ 		"The pg_hba.conf settings data may contains stale information")));
+ 
+ 	rsi->returnMode = SFRM_Materialize;
+ 
+ 	pg_hba_lookup_internal(database, user, &addr, hostname, ssl_inuse, args_mode, rsi);
+ 
+ 	PG_RETURN_NULL();
+ }
+ 
+ 
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 4005,4010 **** PostgresMain(int argc, char *argv[],
--- 4005,4023 ----
  		{
  			got_SIGHUP = false;
  			ProcessConfigFile(PGC_SIGHUP);
+ 			
+ 			/* 
+ 			 * Reload authentication config files too to refresh 
+ 			 * pg_hba_conf view data.
+ 			 */
+ 			if (!load_hba())
+ 			{
+ 				ereport(DEBUG1,
+ 					(errmsg("Falure in reloading pg_hba.conf, pg_hba_conf view may show stale information")));
+ 				load_hba_failure = true;
+ 			}
+ 			
+ 			load_hba_failure = false;
  		}
  
  		/*
*** a/src/backend/utils/adt/jsonb.c
--- b/src/backend/utils/adt/jsonb.c
***************
*** 28,39 ****
  #include "utils/syscache.h"
  #include "utils/typcache.h"
  
- typedef struct JsonbInState
- {
- 	JsonbParseState *parseState;
- 	JsonbValue *res;
- } JsonbInState;
- 
  /* unlike with json categories, we need to treat json and jsonb differently */
  typedef enum					/* type categories for datum_to_jsonb */
  {
--- 28,33 ----
***************
*** 83,89 **** static void jsonb_categorize_type(Oid typoid,
  static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
  			   JsonbTypeCategory tcategory, Oid outfuncoid,
  			   bool key_scalar);
! static void add_jsonb(Datum val, bool is_null, JsonbInState *result,
  		  Oid val_type, bool key_scalar);
  static JsonbParseState *clone_parse_state(JsonbParseState *state);
  static char *JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent);
--- 77,83 ----
  static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
  			   JsonbTypeCategory tcategory, Oid outfuncoid,
  			   bool key_scalar);
! void add_jsonb(Datum val, bool is_null, JsonbInState *result,
  		  Oid val_type, bool key_scalar);
  static JsonbParseState *clone_parse_state(JsonbParseState *state);
  static char *JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent);
***************
*** 1113,1119 **** composite_to_jsonb(Datum composite, JsonbInState *result)
   * lookups only once.
   */
  
! static void
  add_jsonb(Datum val, bool is_null, JsonbInState *result,
  		  Oid val_type, bool key_scalar)
  {
--- 1107,1113 ----
   * lookups only once.
   */
  
! void
  add_jsonb(Datum val, bool is_null, JsonbInState *result,
  		  Oid val_type, bool key_scalar)
  {
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3079,3084 **** DATA(insert OID = 2084 (  pg_show_all_settings	PGNSP PGUID 12 1 1000 0 0 f f f f
--- 3079,3090 ----
  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 = 3997 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v u 2 0 2249 "25 25" "{25,25,23,25,3802,3802,869,25,25,3802,25}" "{i,i,o,o,o,o,o,o,o,o,o}" "{database,user,line_number,type,database,user,address,hostname,method,options,mode}" _null_ _null_ pg_hba_lookup_2args _null_ _null_ _null_));
+ DESCR("view client authentication settings");
+ DATA(insert OID = 3998 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v u 3 0 2249 "25 25 25" "{25,25,25,23,25,3802,3802,869,25,25,3802,25}" "{i,i,i,o,o,o,o,o,o,o,o,o}" "{database,user,address,line_number,type,database,user,address,hostname,method,options,mode}" _null_ _null_ pg_hba_lookup_3args _null_ _null_ _null_));
+ DESCR("view client authentication settings");
+ DATA(insert OID = 3999 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v u 4 0 2249 "25 25 25 16" "{25,25,25,16,23,25,3802,3802,869,25,25,3802,25}" "{i,i,i,i,o,o,o,o,o,o,o,o,o}" "{database,user,address,ssl_inuse,line_number,type,database,user,address,hostname,method,options,mode}" _null_ _null_ pg_hba_lookup _null_ _null_ _null_));
+ DESCR("view client authentication settings");
  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 = 1065 (  pg_prepared_xact PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{28,25,1184,26,26}" "{o,o,o,o,o}" "{transaction,gid,prepared,ownerid,dbid}" _null_ _null_ pg_prepared_xact _null_ _null_ _null_ ));
*** a/src/include/libpq/hba.h
--- b/src/include/libpq/hba.h
***************
*** 96,101 **** typedef struct IdentLine
--- 96,103 ----
  /* kluge to avoid including libpq/libpq-be.h here */
  typedef struct Port hbaPort;
  
+ extern bool load_hba_failure;
+ 
  extern bool load_hba(void);
  extern bool load_ident(void);
  extern void hba_getauthmethod(hbaPort *port);
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 1123,1128 **** extern Datum set_config_by_name(PG_FUNCTION_ARGS);
--- 1123,1133 ----
  extern Datum show_all_settings(PG_FUNCTION_ARGS);
  extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
  
+ /* hba.c */
+ extern Datum pg_hba_lookup_2args(PG_FUNCTION_ARGS);
+ extern Datum pg_hba_lookup_3args(PG_FUNCTION_ARGS);
+ extern Datum pg_hba_lookup(PG_FUNCTION_ARGS);
+ 
  /* rls.c */
  extern Datum row_security_active(PG_FUNCTION_ARGS);
  extern Datum row_security_active_name(PG_FUNCTION_ARGS);
*** a/src/include/utils/json.h
--- b/src/include/utils/json.h
***************
*** 43,49 **** extern Datum json_object(PG_FUNCTION_ARGS);
  extern Datum json_object_two_arg(PG_FUNCTION_ARGS);
  
  extern void escape_json(StringInfo buf, const char *str);
- 
  extern Datum json_typeof(PG_FUNCTION_ARGS);
  
  /* functions in jsonfuncs.c */
--- 43,48 ----
*** a/src/include/utils/jsonb.h
--- b/src/include/utils/jsonb.h
***************
*** 343,348 **** typedef struct JsonbIterator
--- 343,354 ----
  	struct JsonbIterator *parent;
  } JsonbIterator;
  
+ typedef struct JsonbInState
+ {
+ 	JsonbParseState *parseState;
+ 	JsonbValue *res;
+ } JsonbInState;
+ 
  /* I/O routines */
  extern Datum jsonb_in(PG_FUNCTION_ARGS);
  extern Datum jsonb_out(PG_FUNCTION_ARGS);
***************
*** 428,433 **** extern bool JsonbDeepContains(JsonbIterator **val,
--- 434,441 ----
  extern void JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash);
  
  /* jsonb.c support functions */
+ void add_jsonb(Datum val, bool is_null, JsonbInState *result,
+ 	Oid val_type, bool key_scalar);
  extern char *JsonbToCString(StringInfo out, JsonbContainer *in,
  			   int estimated_len);
  extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in,
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
***************
*** 1327,1332 **** pg_group| SELECT pg_authid.rolname AS groname,
--- 1327,1344 ----
            WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist
     FROM pg_authid
    WHERE (NOT pg_authid.rolcanlogin);
+ pg_hba_conf| SELECT a.line_number,
+     a.type,
+     a.keyword_databases,
+     a.databases,
+     a.keyword_users,
+     a.users,
+     a.address,
+     a.compare_method,
+     a.hostname,
+     a.method,
+     a.options
+    FROM pg_hba_conf() a(line_number, type, keyword_databases, databases, keyword_users, users, address, compare_method, hostname, method, options);
  pg_indexes| SELECT n.nspname AS schemaname,
      c.relname AS tablename,
      i.relname AS indexname,
#16Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Pavel Stehule (#13)
1 attachment(s)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Fri, Dec 4, 2015 at 7:45 AM, Pavel Stehule <pavel.stehule@gmail.com> wrote:

this tracing can be implemented to main pg_hba processing. When you are
connect from some specific client - and you can see, why you cannot to
connect to Postgres

The trace messages that are going to print doesn't come to client until the
connection gets successful. The traces may not useful for the clients
to find out
why the connection is failing. But it may be useful for administrators.
How about the attached patch?

[kommih@localhost bin]$ ./psql postgres -h ::1
psql (9.6devel)
Type "help" for help.

postgres=#

ServerLog:
NOTICE: Skipped 84 pg_hba line, because of host connection type.
NOTICE: Skipped 86 pg_hba line, because of non matching IP.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

hba_trace.patchapplication/octet-stream; name=hba_trace.patchDownload
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 1657,1681 **** check_hba(hbaPort *port)
--- 1657,1697 ----
  		if (hba->conntype == ctLocal)
  		{
  			if (!IS_AF_UNIX(port->raddr.addr.ss_family))
+ 			{
+ 				ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of host connection type.",
+ 				 					hba->linenumber)));
  				continue;
+ 			}
  		}
  		else
  		{
  			if (IS_AF_UNIX(port->raddr.addr.ss_family))
+ 			{
+ 				ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of local connection type.",
+ 				 					hba->linenumber)));
  				continue;
+ 			}
  
  			/* Check SSL state */
  			if (port->ssl_in_use)
  			{
  				/* Connection is SSL, match both "host" and "hostssl" */
  				if (hba->conntype == ctHostNoSSL)
+ 				{
+ 					ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of SSL connection.",
+ 					 					hba->linenumber)));
  					continue;
+ 				}
  			}
  			else
  			{
  				/* Connection is not SSL, match both "host" and "hostnossl" */
  				if (hba->conntype == ctHostSSL)
+ 				{
+ 					ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of not SSL connection.",
+ 					 					hba->linenumber)));
  					continue;
+ 				}
  			}
  
  			/* Check IP address */
***************
*** 1686,1699 **** check_hba(hbaPort *port)
--- 1702,1723 ----
  					{
  						if (!check_hostname(port,
  											hba->hostname))
+ 						{
+ 							ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of non matching hostname.",
+ 							 					hba->linenumber)));
  							continue;
+ 						}
  					}
  					else
  					{
  						if (!check_ip(&port->raddr,
  									  (struct sockaddr *) & hba->addr,
  									  (struct sockaddr *) & hba->mask))
+ 						{
+ 							ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of non matching IP.",
+ 							 					hba->linenumber)));
  							continue;
+ 						}
  					}
  					break;
  				case ipCmpAll:
***************
*** 1702,1708 **** check_hba(hbaPort *port)
--- 1726,1736 ----
  				case ipCmpSameNet:
  					if (!check_same_host_or_net(&port->raddr,
  												hba->ip_cmp_method))
+ 					{
+ 						ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of non matching samenet/samehost.",
+ 						 					hba->linenumber)));
  						continue;
+ 					}
  					break;
  				default:
  					/* shouldn't get here, but deem it no-match if so */
***************
*** 1713,1722 **** check_hba(hbaPort *port)
--- 1741,1758 ----
  		/* Check database and role */
  		if (!check_db(port->database_name, port->user_name, roleid,
  					  hba->databases))
+ 		{
+ 			ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of non matching database.",
+ 			 					hba->linenumber)));
  			continue;
+ 		}
  
  		if (!check_role(port->user_name, roleid, hba->roles))
+ 		{
+ 			ereport(NOTICE, (errmsg("Skipped %d pg_hba line, because of non matching role.",
+ 			 					hba->linenumber)));
  			continue;
+ 		}
  
  		/* Found a record that matched! */
  		port->hba = hba;
#17Pavel Stehule
pavel.stehule@gmail.com
In reply to: Haribabu Kommi (#15)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

2015-12-04 5:33 GMT+01:00 Haribabu Kommi <kommi.haribabu@gmail.com>:

On Fri, Dec 4, 2015 at 8:05 AM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:

Here I attached the patch with the suggested changes.
Along with line number, I kept the options column also with

authentication

options as a jsonb datatype.

Example output:

postgres=# select pg_hba_lookup('test','all','::1');
NOTICE: Skipped 84 Hba line, because of non matching IP.
NOTICE: Skipped 86 Hba line, because of non matching database.
NOTICE: Skipped 87 Hba line, because of non matching role.
pg_hba_lookup
---------------
(89,trust,{})
(1 row)

comments?

I don't like this interface. It's nice for psql, but everybody else is
going to lose. I think these should be reported in the SRF result set
as well; perhaps add a "mode" column that says "skipped" for such rows,
and "matched" for the one that, uh, matches. (Please try calling your
function with "select * from" which should give nicer output.)

How about as follows?

postgres=# select * from pg_hba_lookup('all','all','::1');
line_number | type | database | user | address | hostname |
method | options | mode

-------------+-------+----------+---------+-----------+----------+--------+---------+---------
84 | local | ["all"] | ["all"] |
| | trust | {} | skipped
86 | host | ["all"] | ["all"] | 127.0.0.1 |
| trust | {} | skipped
88 | host | ["all"] | ["all"] | ::1
| | trust | {} | matched
(3 rows)

the tabular interface is better, and then NOTICEs are not necessary. I
like to see some info why row was skipped in the table.

Regards

Pavel

Show quoted text

In the above case, all the columns are displayed. Based on the
feedback we can keep
the required columns. I didn't yet removed the NOTICE messages in the
attached version.
Are they still required?

Regards,
Hari Babu
Fujitsu Australia

#18Pavel Stehule
pavel.stehule@gmail.com
In reply to: Haribabu Kommi (#16)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

2015-12-04 5:48 GMT+01:00 Haribabu Kommi <kommi.haribabu@gmail.com>:

On Fri, Dec 4, 2015 at 7:45 AM, Pavel Stehule <pavel.stehule@gmail.com>
wrote:

this tracing can be implemented to main pg_hba processing. When you are
connect from some specific client - and you can see, why you cannot to
connect to Postgres

The trace messages that are going to print doesn't come to client until the
connection gets successful. The traces may not useful for the clients
to find out
why the connection is failing. But it may be useful for administrators.
How about the attached patch?

yes, it is only for admin and should be stored to log

Show quoted text

[kommih@localhost bin]$ ./psql postgres -h ::1
psql (9.6devel)
Type "help" for help.

postgres=#

ServerLog:
NOTICE: Skipped 84 pg_hba line, because of host connection type.
NOTICE: Skipped 86 pg_hba line, because of non matching IP.

Regards,
Hari Babu
Fujitsu Australia

#19Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Haribabu Kommi (#16)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

Haribabu Kommi wrote:

The trace messages that are going to print doesn't come to client until the
connection gets successful. The traces may not useful for the clients
to find out
why the connection is failing. But it may be useful for administrators.
How about the attached patch?

[kommih@localhost bin]$ ./psql postgres -h ::1
psql (9.6devel)
Type "help" for help.

postgres=#

ServerLog:
NOTICE: Skipped 84 pg_hba line, because of host connection type.
NOTICE: Skipped 86 pg_hba line, because of non matching IP.

That's going to be way too noisy. Some applications open dozens of
connections per second -- imagine a dozen NOTICEs per each connection
established. It's going to fill any disk you install as the server log
partition ...

I can imagine worse nightmares, but this one's a pretty ugly one.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#20Pavel Stehule
pavel.stehule@gmail.com
In reply to: Alvaro Herrera (#19)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

2015-12-04 17:16 GMT+01:00 Alvaro Herrera <alvherre@2ndquadrant.com>:

Haribabu Kommi wrote:

The trace messages that are going to print doesn't come to client until

the

connection gets successful. The traces may not useful for the clients
to find out
why the connection is failing. But it may be useful for administrators.
How about the attached patch?

[kommih@localhost bin]$ ./psql postgres -h ::1
psql (9.6devel)
Type "help" for help.

postgres=#

ServerLog:
NOTICE: Skipped 84 pg_hba line, because of host connection type.
NOTICE: Skipped 86 pg_hba line, because of non matching IP.

That's going to be way too noisy. Some applications open dozens of
connections per second -- imagine a dozen NOTICEs per each connection
established. It's going to fill any disk you install as the server log
partition ...

I can imagine worse nightmares, but this one's a pretty ugly one.

It should be disabled by default

only when you have some problems, then you can enable it

Regards

Pavel

Show quoted text

--
Álvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#21Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Haribabu Kommi (#15)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

Haribabu Kommi wrote:

How about as follows?

postgres=# select * from pg_hba_lookup('all','all','::1');
line_number | type | database | user | address | hostname | method | options | mode
-------------+-------+----------+---------+-----------+----------+--------+---------+---------
84 | local | ["all"] | ["all"] | | | trust | {} | skipped
86 | host | ["all"] | ["all"] | 127.0.0.1 | | trust | {} | skipped
88 | host | ["all"] | ["all"] | ::1 | | trust | {} | matched
(3 rows)

What did you do to the whitespace when posting that table? I had to
reformat it pretty heavily to understand what you had.
Anyway, I think the "mode" column should be right after the line number.
I assume the "reason" for skipped lines is going to be somewhere in the
table too.

What happens if a "reject" line is matched? I hope the lookup
would terminate there.

What does it mean to query for "all"? Do you have database and user
named "all"? Because otherwise that seems wrong to me; you should be
able to query for specific databases/users, but not for special
keywords; maybe I am wrong and there is a use case for this, in which
case please state what it is.

I see three problems in your code. One is that the translation of
auth_method enum to text should be a separate function, not the SQL
function layer; another is that the code to put keywords as JSON object
values is way too repetitive; the other is that messing with the JSON
API like that is not nice. (I don't think we're closed to doing that,
but that would be a separate discussion). I think this patch should
just use the "push value" interface rather than expose add_jsonb.

(I assume the usage of JSON rather than a regular array was already
discussed and JSON was chosen for some reason.)

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#22Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Pavel Stehule (#20)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

Pavel Stehule wrote:

It should be disabled by default

only when you have some problems, then you can enable it

That still seems mostly unworkable to me. Are we going to tell DBAs to
set PGOPTIONS when they have some pg_hba problem?

What's the issue with calling the function when you want to research
some problem? Presumably that's the whole point of the function.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#23Pavel Stehule
pavel.stehule@gmail.com
In reply to: Alvaro Herrera (#22)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

2015-12-04 17:34 GMT+01:00 Alvaro Herrera <alvherre@2ndquadrant.com>:

Pavel Stehule wrote:

It should be disabled by default

only when you have some problems, then you can enable it

That still seems mostly unworkable to me. Are we going to tell DBAs to
set PGOPTIONS when they have some pg_hba problem?

why not - it isn't bad idea.

What's the issue with calling the function when you want to research
some problem? Presumably that's the whole point of the function.

sometimes you shouldn't set real parameters - I had to solve some issues
with IP6/IP4 - and I missed debug info on server side.

Regards

Pavel

Show quoted text

--
Álvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#24Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Alvaro Herrera (#21)
1 attachment(s)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Sat, Dec 5, 2015 at 3:31 AM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:

Haribabu Kommi wrote:

How about as follows?

postgres=# select * from pg_hba_lookup('all','all','::1');
line_number | type | database | user | address | hostname | method | options | mode
-------------+-------+----------+---------+-----------+----------+--------+---------+---------
84 | local | ["all"] | ["all"] | | | trust | {} | skipped
86 | host | ["all"] | ["all"] | 127.0.0.1 | | trust | {} | skipped
88 | host | ["all"] | ["all"] | ::1 | | trust | {} | matched
(3 rows)

What did you do to the whitespace when posting that table? I had to
reformat it pretty heavily to understand what you had.
Anyway, I think the "mode" column should be right after the line number.
I assume the "reason" for skipped lines is going to be somewhere in the
table too.

when i try to copy paste the output from psql, it didn't come properly, so
I adjusted the same to looks properly, but after sending mail, it look ugly.

Added reason column also as the last column of the table and moved the mode
as the second column.

What happens if a "reject" line is matched? I hope the lookup
would terminate there.

whenever any line matches with the given arguments, the function stops
processing further lines.

What does it mean to query for "all"? Do you have database and user
named "all"? Because otherwise that seems wrong to me; you should be
able to query for specific databases/users, but not for special
keywords; maybe I am wrong and there is a use case for this, in which
case please state what it is.

The 'all' is just passed as a database and user name. In my configuration
I just put every database to match. so just for a test i did that way. There is
no special handling for keywords.

I see three problems in your code. One is that the translation of
auth_method enum to text should be a separate function, not the SQL
function layer;

Moved into a different function.

another is that the code to put keywords as JSON object
values is way too repetitive; the other is that messing with the JSON
API like that is not nice. (I don't think we're closed to doing that,
but that would be a separate discussion). I think this patch should
just use the "push value" interface rather than expose add_jsonb.

(I assume the usage of JSON rather than a regular array was already
discussed and JSON was chosen for some reason.)

Repetitive jsonb object code is moved into a function and used those functions.
Changed all jsonb calls into push value functions.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_hba_lookup_poc_v5.patchapplication/octet-stream; name=pg_hba_lookup_poc_v5.patchDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 16576,16581 **** SELECT collation for ('foo' COLLATE "de_DE");
--- 16576,16594 ----
         <entry><type>text</type></entry>
         <entry>set parameter and return new value</entry>
        </row>
+ 	  <row>
+        <entry>
+         <indexterm>
+          <primary>pg_hba_lookup</primary>
+         </indexterm>
+         <literal><function>pg_hba_lookup(<parameter>database</> <type>text</>,
+ 							<parameter>user</> <type>text</>
+ 							[, <parameter>address</> <type>text</>]
+ 							[, <parameter>ssl_inuse</> <type>text</>)</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>Returns all row entries of pg_hba.conf till matching entry is found</entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
***************
*** 16633,16638 **** SELECT set_config('log_statement_stats', 'off', false);
--- 16646,16667 ----
  </programlisting>
     </para>
  
+    <para>
+     <function>pg_hba_lookup</function> returns a set of records
+ 	containing the line number, mode, type, database, user, address,
+ 	hostname, method, options and reason. Typical usages include:
+ <programlisting>
+ postgres=# select * from pg_hba_lookup('postgres','kommih', '::1');
+  line_number |  mode   | type  | database |  user   |  address  | hostname | method |      options       |     reason      
+ -------------+---------+-------+----------+---------+-----------+----------+--------+--------------------+-----------------
+           84 | skipped | local | ["all"]  | ["all"] |           |          | trust  | {}                 | non matching IP
+           86 | skipped | host  | ["all"]  | ["all"] | 127.0.0.1 |          | ident  | {"map": "omicron"} | non matching IP
+           88 | matched | host  | ["all"]  | ["all"] | ::1       |          | trust  | {}                 | 
+ (3 rows)
+ </programlisting>
+ 	Only super user can access this function to view the pg_hba entries.
+    </para>
+    
    </sect2>
  
    <sect2 id="functions-admin-signal">
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 25,39 ****
--- 25,45 ----
  #include <arpa/inet.h>
  #include <unistd.h>
  
+ #include "access/htup_details.h"
  #include "catalog/pg_collation.h"
+ #include "catalog/pg_type.h"
+ #include "funcapi.h"
  #include "libpq/ip.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/jsonb.h"
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  
***************
*** 52,57 ****
--- 58,65 ----
  #define MAX_TOKEN	256
  #define MAX_LINE	8192
  
+ #define MAX_LEN_OF_NON_MATCH_HBA_ENTRY_REASON 256
+ 
  /* callback data for check_network_callback */
  typedef struct check_network_data
  {
***************
*** 74,79 **** typedef struct HbaToken
--- 82,97 ----
  	bool		quoted;
  } HbaToken;
  
+ typedef enum hba_lookup_args_mode
+ {
+ 	TWO_ARGS_MODE=0,
+ 	THREE_ARGS_MODE,
+ 	FOUR_ARGS_MODE
+ }hba_lookup_args_mode;
+ 
+ /* Flag to indicate the failure of reloading pg_hba.conf file */
+ bool		load_hba_failure = false;
+ 
  /*
   * pre-parsed content of HBA config file: list of HbaLine structs.
   * parsed_hba_context is the memory context where it lives.
***************
*** 99,104 **** static List *tokenize_inc_file(List *tokens, const char *outer_filename,
--- 117,135 ----
  				  const char *inc_filename);
  static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
  				   int line_num);
+ static Datum getauthmethod(UserAuth	auth_method);
+ static void hba_add_jsonb_string_elem(JsonbParseState **pstate, char *string_elem);
+ static void hba_add_jsonb_string_key(JsonbParseState **pstate, char *string_key);
+ static void hba_add_jsonb_bool_value(JsonbParseState **pstate, bool bool_val);
+ static void hba_add_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val);
+ static void hba_add_jsonb_string_value(JsonbParseState **pstate, char *string_value);
+ static void hba_getvalues_for_line(HbaLine *hba, Datum *values, bool *nulls,
+ 			bool hba_match_result, char *reason);
+ static bool pg_hba_match(HbaLine *hba, char *user, char *database, struct sockaddr_storage *addr,
+ 		 				char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode,
+ 						char *reason);
+ static void pg_hba_lookup_internal(char	*database, char	*user, struct sockaddr_storage *addr,
+ 			char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode, ReturnSetInfo *rsi);
  
  /*
   * isblank() exists in the ISO C99 spec, but it's not very portable yet,
***************
*** 2233,2235 **** hba_getauthmethod(hbaPort *port)
--- 2264,2921 ----
  {
  	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 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 uaLDAP:
+   			result = CStringGetTextDatum("ldap");
+   			break;
+   		case uaCert:
+   			result = CStringGetTextDatum("cert");
+   			break;
+   		case uaRADIUS:
+   			result = CStringGetTextDatum("radius");
+   			break;
+   		case uaPeer:
+   			result = CStringGetTextDatum("peer");
+   			break;
+   		default:
+   			elog(ERROR, "unexpected authentication method in parsed HBA entry");
+   			break;
+   	}
+ 
+   	return result;
+ }
+ 
+ static void
+ hba_add_jsonb_string_elem(JsonbParseState **pstate, char *string_elem)
+ {
+ 	JsonbValue jb;
+ 
+ 	jb.type = jbvString;
+ 	jb.val.string.len = strlen(string_elem);
+ 	jb.val.string.val = pstrdup(string_elem);
+ 	pushJsonbValue(pstate, WJB_ELEM, &jb);
+ }
+ 
+ static void
+ hba_add_jsonb_string_key(JsonbParseState **pstate, char *string_key)
+ {
+ 	JsonbValue jb;
+ 
+ 	jb.type = jbvString;
+ 	jb.val.string.len = strlen(string_key);
+ 	jb.val.string.val = pstrdup(string_key);
+ 	pushJsonbValue(pstate, WJB_KEY, &jb);
+ }
+ 
+ static void
+ hba_add_jsonb_bool_value(JsonbParseState **pstate, bool bool_val)
+ {
+ 	JsonbValue jb;
+ 
+ 	jb.type = jbvBool;
+ 	jb.val.boolean = bool_val;
+ 	pushJsonbValue(pstate, WJB_VALUE, &jb);
+ }
+ 
+ static void
+ hba_add_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val)
+ {
+ 	JsonbValue jb;
+ 	char outputstr[64];
+ 
+ 	sprintf(outputstr, "%d", int32_val);
+ 	jb.type = jbvNumeric;
+ 	jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1));
+ 	pushJsonbValue(pstate, WJB_VALUE, &jb);
+ }
+ 
+ static void
+ hba_add_jsonb_string_value(JsonbParseState **pstate, char *string_value)
+ {
+ 	JsonbValue jb;
+ 
+ 	jb.type = jbvString;
+ 	jb.val.string.len = strlen(string_value);
+ 	jb.val.string.val = pstrdup(string_value);
+ 	pushJsonbValue(pstate, WJB_VALUE, &jb);
+ }
+ 
+ /*
+  * Fill in suitable values to build a tuple representing the
+  * HbaLine provided
+  */
+ static void
+ hba_getvalues_for_line(HbaLine *hba, Datum *values, bool *nulls, bool hba_match_result,
+ 					   char *reason)
+ {
+ 	ListCell   *dbcell;
+ 	char		buffer[NI_MAXHOST];
+ 	int			index = 0;
+ 	JsonbParseState *parseState = NULL;
+ 	JsonbValue *result;
+ 
+ 	/* line_number */
+ 	values[index] = Int32GetDatum(hba->linenumber);
+ 
+ 	/* mode */
+ 	index++;
+ 	if (hba_match_result)
+ 		values[index] = CStringGetTextDatum("matched");
+ 	else
+ 		values[index] = CStringGetTextDatum("skipped");
+ 
+ 	/* 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;
+  		default:
+  			elog(ERROR, "unexpected connection type in parsed HBA entry");
+  			break;
+  	}
+ 
+ 	/* database */
+ 	index++;
+ 	result = pushJsonbValue(&parseState, WJB_BEGIN_ARRAY, NULL);
+ 
+ 	if (list_length(hba->databases) != 0)
+ 	{
+ 		HbaToken   *tok;
+ 
+ 		foreach(dbcell, hba->databases)
+ 		{
+ 			tok = lfirst(dbcell);
+ 			hba_add_jsonb_string_elem(&parseState, tok->string);
+ 		}
+ 	}
+ 
+ 	result = pushJsonbValue(&parseState, WJB_END_ARRAY, NULL);
+ 	values[index] = PointerGetDatum(JsonbValueToJsonb(result));
+ 
+ 	/* user */
+ 	index++;
+ 	result = pushJsonbValue(&parseState, WJB_BEGIN_ARRAY, NULL);
+ 
+ 	if (list_length(hba->roles) != 0)
+ 	{
+ 		HbaToken   *tok;
+ 
+ 		foreach(dbcell, hba->roles)
+ 		{
+ 			tok = lfirst(dbcell);
+ 			hba_add_jsonb_string_elem(&parseState, tok->string);
+ 		}
+ 	}
+ 
+ 	result = pushJsonbValue(&parseState, WJB_END_ARRAY, NULL);
+ 	values[index] = PointerGetDatum(JsonbValueToJsonb(result));
+ 
+ 	/* address */
+ 	index++;
+ 	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+ 	}
+ 	else
+ 		nulls[index] = true;
+ 
+ 	/* hostname */
+ 	index++;
+ 	if (hba->hostname)
+ 		values[index] = CStringGetTextDatum(hba->hostname);
+ 	else
+ 		nulls[index] = true;
+ 
+ 	/* method */
+  	index++;
+  	values[index]= getauthmethod(hba->auth_method);
+ 
+ 	/* options */
+ 	index++;
+ 	result = pushJsonbValue(&parseState, WJB_BEGIN_OBJECT, NULL);
+ 
+ 	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
+ 	{
+ 		if (hba->include_realm)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "include_realm");
+ 			hba_add_jsonb_bool_value(&parseState, true);
+ 		}
+ 
+ 		if (hba->krb_realm)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "krb_realm");
+ 			hba_add_jsonb_string_value(&parseState, hba->krb_realm);
+ 		}
+ 	}
+ 
+ 	if (hba->usermap)
+ 	{
+ 		hba_add_jsonb_string_key(&parseState, "map");
+ 		hba_add_jsonb_string_value(&parseState, hba->usermap);
+ 	}
+ 
+ 	if (hba->clientcert)
+ 	{
+ 		hba_add_jsonb_string_key(&parseState, "clientcert");
+ 		hba_add_jsonb_bool_value(&parseState, true);
+ 	}
+ 
+ 	if (hba->pamservice)
+ 	{
+ 		hba_add_jsonb_string_key(&parseState, "pamservice");
+ 		hba_add_jsonb_string_value(&parseState, hba->pamservice);
+ 	}
+ 
+ 	if (hba->auth_method == uaLDAP)
+ 	{
+ 		if (hba->ldapserver)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapserver");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapserver);
+ 		}
+ 
+ 		if (hba->ldapport)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapport");
+ 			hba_add_jsonb_int32_value(&parseState, hba->ldapport);
+ 		}
+ 
+ 		if (hba->ldaptls)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldaptls");
+ 			hba_add_jsonb_bool_value(&parseState, true);
+ 		}
+ 
+ 		if (hba->ldapprefix)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapprefix");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapprefix);
+ 		}
+ 
+ 		if (hba->ldapsuffix)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapsuffix");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapsuffix);
+ 		}
+ 
+ 		if (hba->ldapbasedn)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapbasedn");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapbasedn);
+ 		}
+ 
+ 		if (hba->ldapbinddn)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapbinddn");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapbinddn);
+ 		}
+ 
+ 		if (hba->ldapbindpasswd)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapbindpasswd");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapbindpasswd);
+ 		}
+ 
+ 		if (hba->ldapsearchattribute)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapsearchattribute");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapsearchattribute);
+ 		}
+ 
+ 		if (hba->ldapscope)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapscope");
+ 			hba_add_jsonb_int32_value(&parseState, hba->ldapscope);
+ 		}
+ 	}
+ 
+ 	if (hba->auth_method == uaRADIUS)
+ 	{
+ 		if (hba->radiusserver)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "radiusserver");
+ 			hba_add_jsonb_string_value(&parseState, hba->radiusserver);
+ 		}
+ 
+ 		if (hba->radiussecret)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "radiussecret");
+ 			hba_add_jsonb_string_value(&parseState, hba->radiussecret);
+ 		}
+ 
+ 		if (hba->radiusidentifier)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "radiusidentifier");
+ 			hba_add_jsonb_string_value(&parseState, hba->radiusidentifier);
+ 		}
+ 
+ 		if (hba->radiusport)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "radiusport");
+ 			hba_add_jsonb_int32_value(&parseState, hba->radiusport);
+ 		}
+ 	}
+ 
+ 	result = pushJsonbValue(&parseState, WJB_END_OBJECT, NULL);
+ 	values[index] = PointerGetDatum(JsonbValueToJsonb(result));
+ 
+ 	/* reason */
+ 	index++;
+ 	if (!hba_match_result)
+ 		values[index] = CStringGetTextDatum(reason);
+ 	else
+ 		nulls[index] = true;
+ }
+ 
+ static bool
+ pg_hba_match(HbaLine *hba, char *database, char *user, struct sockaddr_storage *addr,
+ 			char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode, char *reason)
+ {
+ 	Oid			roleid;
+ 
+ 	if (ssl_inuse)
+ 	{
+ #ifdef USE_SSL
+ 		if (EnableSSL && hba->conntype != ctHostSSL)
+ 		{
+ 			strcpy(reason, "non SSL connection type");
+ 			return false;
+ 		}
+ 		else
+ 		{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 					 errmsg("hostssl requires SSL to be turned on"),
+ 					 errhint("Set ssl = on in postgresql.conf.")));
+ 			return false;
+ 		}
+ #else
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 				 errmsg("hostssl is not supported by this build"),
+ 		  errhint("Compile with --with-openssl to use SSL connections.")));
+ 		return false;
+ #endif
+ 	}
+ 
+ 	/* Get the target role's OID.  Note we do not error out for bad role. */
+ 	roleid = get_role_oid(user, true);
+ 
+ 	if (!check_db(database, user, roleid, hba->databases))
+ 	{
+ 		strcpy(reason, "non matching database");
+ 		return false;
+ 	}
+ 
+ 	if (!check_role(user, roleid, hba->roles))
+ 	{
+ 		strcpy(reason, "non matching role");
+ 		return false;
+ 	}
+ 
+ 	if (args_mode != TWO_ARGS_MODE)
+ 	{
+ 		/* Check IP address */
+ 		switch (hba->ip_cmp_method)
+ 		{
+ 			case ipCmpMask:
+ 				if (hostname && hba->hostname)
+ 				{
+ 					if (!hostname_match(hostname, hba->hostname))
+ 					{
+ 						strcpy(reason, "non matching hostname");
+ 						return false;
+ 					}
+ 				}
+ 				else
+ 				{
+ 					if (!check_ip((SockAddr *)addr,
+ 								  (struct sockaddr *) & hba->addr,
+ 								  (struct sockaddr *) & hba->mask))
+ 					{
+ 						strcpy(reason, "non matching IP");
+ 						return false;
+ 					}
+ 				}
+ 				break;
+ 			case ipCmpAll:
+ 				break;
+ 			case ipCmpSameHost:
+ 			case ipCmpSameNet:
+ 				if (!check_same_host_or_net((SockAddr *)addr,
+ 											hba->ip_cmp_method))
+ 				{
+ 					strcpy(reason, "non matching SameHost/SameNet");
+ 					return false;
+ 				}
+ 				break;
+ 			default: /* Not reachable case*/
+ 				break;
+ 		}
+ 	}
+ 
+ 	return true;
+ }
+ 
+ 
+ #define NUM_PG_HBA_CONF_ATTS   10
+ 
+ static void
+ pg_hba_lookup_internal(char *database, char *user, struct sockaddr_storage *addr,
+ 		char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode, ReturnSetInfo *rsi)
+ {
+ 	Tuplestorestate *tuple_store;
+ 	TupleDesc	tupdesc;
+ 	ListCell   *line;
+ 	MemoryContext old_cxt;
+ 
+ 	/*
+ 	* Create the tupledesc and tuplestore in the per_query context as
+ 	* required for SFRM_Materialize.
+ 	*/
+ 	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+ 
+ 	tupdesc = CreateTemplateTupleDesc(NUM_PG_HBA_CONF_ATTS, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)1, "line_number",
+ 		INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)2, "mode",
+ 		TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)3, "type",
+ 		TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)4, "database",
+ 		JSONBOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)5, "user",
+ 		JSONBOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)6, "address",
+ 		INETOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)7, "hostname",
+ 		TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)8, "method",
+ 		TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)9, "options",
+ 		JSONBOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber)10, "reason",
+ 		TEXTOID, -1, 0);
+ 	BlessTupleDesc(tupdesc);
+ 
+ 	tuple_store =
+ 		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+ 		false, work_mem);
+ 
+ 	MemoryContextSwitchTo(old_cxt);
+ 
+ 	/*
+ 	* Loop through the list and deparse each entry as it comes, storing it in
+ 	* the tuplestore. Any temporary memory allocations here live only for the
+ 	* function call lifetime.
+ 	*/
+ 	foreach(line, parsed_hba_lines)
+ 	{
+ 		HbaLine    *hba = (HbaLine *)lfirst(line);
+ 		Datum		values[NUM_PG_HBA_CONF_ATTS];
+ 		bool		nulls[NUM_PG_HBA_CONF_ATTS];
+ 		HeapTuple	tuple;
+ 		bool hba_match_result;
+ 		char reason[MAX_LEN_OF_NON_MATCH_HBA_ENTRY_REASON];
+ 
+ 		memset(values, 0, sizeof(values));
+ 		memset(nulls, 0, sizeof(nulls));
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		hba_match_result = pg_hba_match(hba, database, user, addr, hostname,
+ 										ssl_inuse, args_mode, reason);
+ 
+ 		/* Get the next parsed hba line values */
+ 		hba_getvalues_for_line(hba, values, nulls, hba_match_result, reason);
+ 
+ 		/* build a tuple */
+ 		tuple = heap_form_tuple(tupdesc, values, nulls);
+ 		tuplestore_puttuple(tuple_store, tuple);
+ 
+ 		/* Stop the loop processing, if matching entry is found */
+ 		if (hba_match_result)
+ 			break;
+ 	}
+ 
+ 	rsi->setDesc = tupdesc;
+ 	rsi->setResult = tuple_store;
+ 	return;
+ }
+ 
+ /*
+ * SQL-accessible SRF to return all the settings from the pg_hba.conf
+ * file.
+ */
+ Datum
+ pg_hba_lookup_2args(PG_FUNCTION_ARGS)
+ {
+ 	return pg_hba_lookup(fcinfo);
+ }
+ 
+ /*
+  * SQL-accessible SRF to return all the settings from the pg_hba.conf
+  * file.
+  */
+ Datum
+ pg_hba_lookup_3args(PG_FUNCTION_ARGS)
+ {
+ 	return pg_hba_lookup(fcinfo);
+ }
+ 
+ /*
+  * SQL-accessible SRF to return all the settings from the pg_hba.conf
+  * file. 
+  */
+ Datum
+ pg_hba_lookup(PG_FUNCTION_ARGS)
+ {
+ 	char	*user;
+ 	char	*database;
+ 	char	*address;
+ 	char    *hostname;
+ 	bool	ssl_inuse = false;
+ 	struct sockaddr_storage addr;
+ 	hba_lookup_args_mode args_mode = TWO_ARGS_MODE; /* Minimum number of arguments */
+ 
+ 	/*
+ 	* 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.
+ 	*/
+ 	ReturnSetInfo *rsi = (ReturnSetInfo *)fcinfo->resultinfo;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("only superuser can view pg_hba.conf settings"))));
+ 
+ 	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+ 		(rsi->allowedModes & SFRM_Materialize) == 0)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				errmsg("set-valued function called in context that "
+ 						"cannot accept a set")));
+ 
+ 	if (PG_ARGISNULL(0))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 				(errmsg("database name is required to match pg_hba configuration entry"))));
+ 	else
+ 		database = TextDatumGetCString(PG_GETARG_DATUM(0));
+ 
+ 	if (PG_ARGISNULL(1))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 				(errmsg("user name is required to match pg_hba configuration entry"))));
+ 	else
+ 		user = TextDatumGetCString(PG_GETARG_DATUM(1));
+ 
+ 	if (PG_NARGS() != 2)
+ 	{
+ 		struct addrinfo *gai_result;
+ 		struct addrinfo hints;
+ 		int ret;
+ 
+ 		args_mode = THREE_ARGS_MODE;
+ 		if (!PG_ARGISNULL(2))
+ 			address = TextDatumGetCString(PG_GETARG_DATUM(2));
+ 
+ 		/* Get the IP address either way */
+ 		hints.ai_flags = AI_NUMERICHOST;
+ 		hints.ai_family = AF_UNSPEC;
+ 		hints.ai_socktype = 0;
+ 		hints.ai_protocol = 0;
+ 		hints.ai_addrlen = 0;
+ 		hints.ai_canonname = NULL;
+ 		hints.ai_addr = NULL;
+ 		hints.ai_next = NULL;
+ 
+ 		ret = pg_getaddrinfo_all(address, NULL, &hints, &gai_result);
+ 		if (ret == 0 && gai_result)
+ 			memcpy(&addr, gai_result->ai_addr, gai_result->ai_addrlen);
+ 		else if (ret == EAI_NONAME)
+ 			hostname = pstrdup(address);
+ 		else
+ 		{
+ 			ereport(ERROR,
+ 					(errmsg("invalid IP address \"%s\": %s",
+ 							 address, gai_strerror(ret))));
+ 			if (gai_result)
+ 				pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ 			return false;
+ 		}
+ 
+ 		pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ 
+ 		if (PG_NARGS() != 3)
+ 		{
+ 			args_mode = FOUR_ARGS_MODE;
+ 			if (!PG_ARGISNULL(3))
+ 				ssl_inuse = DatumGetBool(PG_GETARG_DATUM(3));
+ 		}
+ 	}
+ 
+ 	if (load_hba_failure)
+ 		ereport(WARNING,
+ 		(errmsg("There was some failure in reloading pg_hba.conf file. "
+ 		"The pg_hba.conf settings data may contains stale information")));
+ 
+ 	rsi->returnMode = SFRM_Materialize;
+ 
+ 	pg_hba_lookup_internal(database, user, &addr, hostname, ssl_inuse, args_mode, rsi);
+ 
+ 	PG_RETURN_NULL();
+ }
+ 
+ 
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 4005,4010 **** PostgresMain(int argc, char *argv[],
--- 4005,4023 ----
  		{
  			got_SIGHUP = false;
  			ProcessConfigFile(PGC_SIGHUP);
+ 			
+ 			/* 
+ 			 * Reload authentication config files too to refresh 
+ 			 * pg_hba_conf view data.
+ 			 */
+ 			if (!load_hba())
+ 			{
+ 				ereport(DEBUG1,
+ 					(errmsg("Falure in reloading pg_hba.conf, pg_hba_conf view may show stale information")));
+ 				load_hba_failure = true;
+ 			}
+ 			
+ 			load_hba_failure = false;
  		}
  
  		/*
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3079,3084 **** DATA(insert OID = 2084 (  pg_show_all_settings	PGNSP PGUID 12 1 1000 0 0 f f f f
--- 3079,3090 ----
  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 = 3997 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v u 2 0 2249 "25 25" "{25,25,23,25,25,3802,3802,869,25,25,3802,25}" "{i,i,o,o,o,o,o,o,o,o,o,o}" "{database,user,line_number,mode,type,database,user,address,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup_2args _null_ _null_ _null_));
+ DESCR("view client authentication settings");
+ DATA(insert OID = 3998 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v u 3 0 2249 "25 25 25" "{25,25,25,23,25,25,3802,3802,869,25,25,3802,25}" "{i,i,i,o,o,o,o,o,o,o,o,o,o}" "{database,user,address,line_number,mode,type,database,user,address,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup_3args _null_ _null_ _null_));
+ DESCR("view client authentication settings");
+ DATA(insert OID = 3999 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v u 4 0 2249 "25 25 25 16" "{25,25,25,16,23,25,25,3802,3802,869,25,25,3802,25}" "{i,i,i,i,o,o,o,o,o,o,o,o,o,o}" "{database,user,address,ssl_inuse,line_number,mode,type,database,user,address,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup _null_ _null_ _null_));
+ DESCR("view client authentication settings");
  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 = 1065 (  pg_prepared_xact PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{28,25,1184,26,26}" "{o,o,o,o,o}" "{transaction,gid,prepared,ownerid,dbid}" _null_ _null_ pg_prepared_xact _null_ _null_ _null_ ));
*** a/src/include/libpq/hba.h
--- b/src/include/libpq/hba.h
***************
*** 96,101 **** typedef struct IdentLine
--- 96,103 ----
  /* kluge to avoid including libpq/libpq-be.h here */
  typedef struct Port hbaPort;
  
+ extern bool load_hba_failure;
+ 
  extern bool load_hba(void);
  extern bool load_ident(void);
  extern void hba_getauthmethod(hbaPort *port);
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 1123,1128 **** extern Datum set_config_by_name(PG_FUNCTION_ARGS);
--- 1123,1133 ----
  extern Datum show_all_settings(PG_FUNCTION_ARGS);
  extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
  
+ /* hba.c */
+ extern Datum pg_hba_lookup_2args(PG_FUNCTION_ARGS);
+ extern Datum pg_hba_lookup_3args(PG_FUNCTION_ARGS);
+ extern Datum pg_hba_lookup(PG_FUNCTION_ARGS);
+ 
  /* rls.c */
  extern Datum row_security_active(PG_FUNCTION_ARGS);
  extern Datum row_security_active_name(PG_FUNCTION_ARGS);
#25Amit Kapila
amit.kapila16@gmail.com
In reply to: Haribabu Kommi (#24)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Tue, Dec 8, 2015 at 9:41 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Sat, Dec 5, 2015 at 3:31 AM, Alvaro Herrera <alvherre@2ndquadrant.com>

wrote:

Haribabu Kommi wrote:

How about as follows?

postgres=# select * from pg_hba_lookup('all','all','::1');
line_number | type | database | user | address | hostname |

method | options | mode

-------------+-------+----------+---------+-----------+----------+--------+---------+---------

84 | local | ["all"] | ["all"] | | |

trust | {} | skipped

86 | host | ["all"] | ["all"] | 127.0.0.1 | |

trust | {} | skipped

88 | host | ["all"] | ["all"] | ::1 | |

trust | {} | matched

(3 rows)

What did you do to the whitespace when posting that table? I had to
reformat it pretty heavily to understand what you had.
Anyway, I think the "mode" column should be right after the line number.
I assume the "reason" for skipped lines is going to be somewhere in the
table too.

when i try to copy paste the output from psql, it didn't come properly, so
I adjusted the same to looks properly, but after sending mail, it look

ugly.

Added reason column also as the last column of the table and moved the

mode

as the second column.

Few assorted comments:

1.
+ /*
+ * SQL-accessible SRF to return all the settings from the pg_hba.conf
+ * file.
+ */
+ Datum
+ pg_hba_lookup_2args(PG_FUNCTION_ARGS)
+ {
+ return pg_hba_lookup(fcinfo);
+ }
+
+ /*
+  * SQL-accessible SRF to return all the settings from the pg_hba.conf
+  * file.
+  */
+ Datum
+ pg_hba_lookup_3args(PG_FUNCTION_ARGS)
+ {
+ return pg_hba_lookup(fcinfo);
+ }

I think it is better to have check on number of args in the
above functions similar to what we have in ginarrayextract_2args.

2.
+
+ /*
+ * Reload authentication config files too to refresh
+ * pg_hba_conf view data.
+ */
+ if (!load_hba())
+ {
+ ereport(DEBUG1,
+ (errmsg("Falure in reloading pg_hba.conf, pg_hba_conf view may show stale
information")));
+ load_hba_failure = true;
+ }
+
+ load_hba_failure = false;

Won't the above code set load_hba_failure as false even in
failure case.

3.
+ Datum
+ pg_hba_lookup(PG_FUNCTION_ARGS)
+ {
+ char *user;
+ char *database;
+ char *address;
+ char    *hostname;
+ bool ssl_inuse = false;
+ struct sockaddr_storage addr;
+ hba_lookup_args_mode args_mode = TWO_ARGS_MODE; /* Minimum number of
arguments */
+
+ /*
+ * 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.
+ */
+ ReturnSetInfo *rsi = (ReturnSetInfo *)fcinfo->resultinfo;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("only superuser can view pg_hba.conf settings"))));

To be consistent with other similar messages, it is better to
start this message with "must be superuser ..", refer other
similar superuser checks in xlogfuncs.c

With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#26Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Amit Kapila (#25)
1 attachment(s)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Wed, Dec 9, 2015 at 2:36 AM, Amit Kapila <amit.kapila16@gmail.com> wrote:

Few assorted comments:

Thanks for the review.

1.
+ /*
+ * SQL-accessible SRF to return all the settings from the pg_hba.conf
+ * file.
+ */
+ Datum
+ pg_hba_lookup_2args(PG_FUNCTION_ARGS)
+ {
+ return pg_hba_lookup(fcinfo);
+ }
+
+ /*
+  * SQL-accessible SRF to return all the settings from the pg_hba.conf
+  * file.
+  */
+ Datum
+ pg_hba_lookup_3args(PG_FUNCTION_ARGS)
+ {
+ return pg_hba_lookup(fcinfo);
+ }

I think it is better to have check on number of args in the
above functions similar to what we have in ginarrayextract_2args.

ginarrayextract_2args is an deprecated function that checks and returns
error if user is using with two arguments. But in pg_hba_lookup function,
providing two argument is a valid scenario. The check can be added only
to verify whether the provided number of arguments are two or not. Is it
really required?

2.
+
+ /*
+ * Reload authentication config files too to refresh
+ * pg_hba_conf view data.
+ */
+ if (!load_hba())
+ {
+ ereport(DEBUG1,
+ (errmsg("Falure in reloading pg_hba.conf, pg_hba_conf view may show stale
information")));
+ load_hba_failure = true;
+ }
+
+ load_hba_failure = false;

Won't the above code set load_hba_failure as false even in
failure case.

Fixed.

3.
+ Datum
+ pg_hba_lookup(PG_FUNCTION_ARGS)
+ {
+ char *user;
+ char *database;
+ char *address;
+ char    *hostname;
+ bool ssl_inuse = false;
+ struct sockaddr_storage addr;
+ hba_lookup_args_mode args_mode = TWO_ARGS_MODE; /* Minimum number of
arguments */
+
+ /*
+ * 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.
+ */
+ ReturnSetInfo *rsi = (ReturnSetInfo *)fcinfo->resultinfo;
+
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("only superuser can view pg_hba.conf settings"))));

To be consistent with other similar messages, it is better to
start this message with "must be superuser ..", refer other
similar superuser checks in xlogfuncs.c

Updated as "must be superuser to view".

Attached updated patch after taking care of review comments.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_hba_lookup_poc_v6.patchapplication/octet-stream; name=pg_hba_lookup_poc_v6.patchDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 16576,16581 **** SELECT collation for ('foo' COLLATE "de_DE");
--- 16576,16594 ----
         <entry><type>text</type></entry>
         <entry>set parameter and return new value</entry>
        </row>
+ 	  <row>
+        <entry>
+         <indexterm>
+          <primary>pg_hba_lookup</primary>
+         </indexterm>
+         <literal><function>pg_hba_lookup(<parameter>database</> <type>text</>,
+ 							<parameter>user</> <type>text</>
+ 							[, <parameter>address</> <type>text</>]
+ 							[, <parameter>ssl_inuse</> <type>text</>)</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>Returns all row entries of pg_hba.conf till matching entry is found</entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
***************
*** 16633,16638 **** SELECT set_config('log_statement_stats', 'off', false);
--- 16646,16667 ----
  </programlisting>
     </para>
  
+    <para>
+     <function>pg_hba_lookup</function> returns a set of records
+ 	containing the line number, mode, type, database, user, address,
+ 	hostname, method, options and reason. Typical usages include:
+ <programlisting>
+ postgres=# select * from pg_hba_lookup('postgres','kommih', '::1');
+  line_number |  mode   | type  | database |  user   |  address  | hostname | method |      options       |     reason      
+ -------------+---------+-------+----------+---------+-----------+----------+--------+--------------------+-----------------
+           84 | skipped | local | ["all"]  | ["all"] |           |          | trust  | {}                 | non matching IP
+           86 | skipped | host  | ["all"]  | ["all"] | 127.0.0.1 |          | ident  | {"map": "omicron"} | non matching IP
+           88 | matched | host  | ["all"]  | ["all"] | ::1       |          | trust  | {}                 | 
+ (3 rows)
+ </programlisting>
+ 	Only super user can access this function to view the pg_hba entries.
+    </para>
+    
    </sect2>
  
    <sect2 id="functions-admin-signal">
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 25,39 ****
--- 25,45 ----
  #include <arpa/inet.h>
  #include <unistd.h>
  
+ #include "access/htup_details.h"
  #include "catalog/pg_collation.h"
+ #include "catalog/pg_type.h"
+ #include "funcapi.h"
  #include "libpq/ip.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/jsonb.h"
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  
***************
*** 52,57 ****
--- 58,65 ----
  #define MAX_TOKEN	256
  #define MAX_LINE	8192
  
+ #define MAX_LEN_OF_NON_MATCH_HBA_ENTRY_REASON 256
+ 
  /* callback data for check_network_callback */
  typedef struct check_network_data
  {
***************
*** 74,79 **** typedef struct HbaToken
--- 82,97 ----
  	bool		quoted;
  } HbaToken;
  
+ typedef enum hba_lookup_args_mode
+ {
+ 	TWO_ARGS_MODE = 0,
+ 	THREE_ARGS_MODE,
+ 	FOUR_ARGS_MODE
+ }	hba_lookup_args_mode;
+ 
+ /* Flag to indicate the failure of reloading pg_hba.conf file */
+ bool		load_hba_failure = false;
+ 
  /*
   * pre-parsed content of HBA config file: list of HbaLine structs.
   * parsed_hba_context is the memory context where it lives.
***************
*** 99,104 **** static List *tokenize_inc_file(List *tokens, const char *outer_filename,
--- 117,135 ----
  				  const char *inc_filename);
  static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
  				   int line_num);
+ static Datum getauthmethod(UserAuth auth_method);
+ static void hba_add_jsonb_string_elem(JsonbParseState **pstate, char *string_elem);
+ static void hba_add_jsonb_string_key(JsonbParseState **pstate, char *string_key);
+ static void hba_add_jsonb_bool_value(JsonbParseState **pstate, bool bool_val);
+ static void hba_add_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val);
+ static void hba_add_jsonb_string_value(JsonbParseState **pstate, char *string_value);
+ static void hba_getvalues_for_line(HbaLine *hba, Datum *values, bool *nulls,
+ 					   bool hba_match_result, char *reason);
+ static bool pg_hba_match(HbaLine *hba, char *user, char *database, struct sockaddr_storage * addr,
+ 			 char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode,
+ 			 char *reason);
+ static void pg_hba_lookup_internal(char *database, char *user, struct sockaddr_storage * addr,
+ 					   char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode, ReturnSetInfo *rsi);
  
  /*
   * isblank() exists in the ISO C99 spec, but it's not very portable yet,
***************
*** 2233,2235 **** hba_getauthmethod(hbaPort *port)
--- 2264,2922 ----
  {
  	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 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 uaLDAP:
+ 			result = CStringGetTextDatum("ldap");
+ 			break;
+ 		case uaCert:
+ 			result = CStringGetTextDatum("cert");
+ 			break;
+ 		case uaRADIUS:
+ 			result = CStringGetTextDatum("radius");
+ 			break;
+ 		case uaPeer:
+ 			result = CStringGetTextDatum("peer");
+ 			break;
+ 		default:
+ 			elog(ERROR, "unexpected authentication method in parsed HBA entry");
+ 			break;
+ 	}
+ 
+ 	return result;
+ }
+ 
+ static void
+ hba_add_jsonb_string_elem(JsonbParseState **pstate, char *string_elem)
+ {
+ 	JsonbValue	jb;
+ 
+ 	jb.type = jbvString;
+ 	jb.val.string.len = strlen(string_elem);
+ 	jb.val.string.val = pstrdup(string_elem);
+ 	pushJsonbValue(pstate, WJB_ELEM, &jb);
+ }
+ 
+ static void
+ hba_add_jsonb_string_key(JsonbParseState **pstate, char *string_key)
+ {
+ 	JsonbValue	jb;
+ 
+ 	jb.type = jbvString;
+ 	jb.val.string.len = strlen(string_key);
+ 	jb.val.string.val = pstrdup(string_key);
+ 	pushJsonbValue(pstate, WJB_KEY, &jb);
+ }
+ 
+ static void
+ hba_add_jsonb_bool_value(JsonbParseState **pstate, bool bool_val)
+ {
+ 	JsonbValue	jb;
+ 
+ 	jb.type = jbvBool;
+ 	jb.val.boolean = bool_val;
+ 
+ 	pushJsonbValue(pstate, WJB_VALUE, &jb);
+ }
+ 
+ static void
+ hba_add_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val)
+ {
+ 	JsonbValue	jb;
+ 	char		outputstr[64];
+ 
+ 	sprintf(outputstr, "%d", int32_val);
+ 	jb.type = jbvNumeric;
+ 	jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1));
+ 
+ 	pushJsonbValue(pstate, WJB_VALUE, &jb);
+ }
+ 
+ static void
+ hba_add_jsonb_string_value(JsonbParseState **pstate, char *string_value)
+ {
+ 	JsonbValue	jb;
+ 
+ 	jb.type = jbvString;
+ 	jb.val.string.len = strlen(string_value);
+ 	jb.val.string.val = pstrdup(string_value);
+ 	pushJsonbValue(pstate, WJB_VALUE, &jb);
+ }
+ 
+ /*
+  * Fill in suitable values to build a tuple representing the
+  * HbaLine provided
+  */
+ static void
+ hba_getvalues_for_line(HbaLine *hba, Datum *values, bool *nulls, bool hba_match_result,
+ 					   char *reason)
+ {
+ 	ListCell   *dbcell;
+ 	char		buffer[NI_MAXHOST];
+ 	int			index = 0;
+ 	JsonbParseState *parseState = NULL;
+ 	JsonbValue *result;
+ 
+ 	/* line_number */
+ 	values[index] = Int32GetDatum(hba->linenumber);
+ 
+ 	/* mode */
+ 	index++;
+ 	if (hba_match_result)
+ 		values[index] = CStringGetTextDatum("matched");
+ 	else
+ 		values[index] = CStringGetTextDatum("skipped");
+ 
+ 	/* 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;
+ 		default:
+ 			elog(ERROR, "unexpected connection type in parsed HBA entry");
+ 			break;
+ 	}
+ 
+ 	/* database */
+ 	index++;
+ 	result = pushJsonbValue(&parseState, WJB_BEGIN_ARRAY, NULL);
+ 
+ 	if (list_length(hba->databases) != 0)
+ 	{
+ 		HbaToken   *tok;
+ 
+ 		foreach(dbcell, hba->databases)
+ 		{
+ 			tok = lfirst(dbcell);
+ 			hba_add_jsonb_string_elem(&parseState, tok->string);
+ 		}
+ 	}
+ 
+ 	result = pushJsonbValue(&parseState, WJB_END_ARRAY, NULL);
+ 	values[index] = PointerGetDatum(JsonbValueToJsonb(result));
+ 
+ 	/* user */
+ 	index++;
+ 	result = pushJsonbValue(&parseState, WJB_BEGIN_ARRAY, NULL);
+ 
+ 	if (list_length(hba->roles) != 0)
+ 	{
+ 		HbaToken   *tok;
+ 
+ 		foreach(dbcell, hba->roles)
+ 		{
+ 			tok = lfirst(dbcell);
+ 			hba_add_jsonb_string_elem(&parseState, tok->string);
+ 		}
+ 	}
+ 
+ 	result = pushJsonbValue(&parseState, WJB_END_ARRAY, NULL);
+ 	values[index] = PointerGetDatum(JsonbValueToJsonb(result));
+ 
+ 	/* address */
+ 	index++;
+ 	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+ 	}
+ 	else
+ 		nulls[index] = true;
+ 
+ 	/* hostname */
+ 	index++;
+ 	if (hba->hostname)
+ 		values[index] = CStringGetTextDatum(hba->hostname);
+ 	else
+ 		nulls[index] = true;
+ 
+ 	/* method */
+ 	index++;
+ 	values[index] = getauthmethod(hba->auth_method);
+ 
+ 	/* options */
+ 	index++;
+ 	result = pushJsonbValue(&parseState, WJB_BEGIN_OBJECT, NULL);
+ 
+ 	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
+ 	{
+ 		if (hba->include_realm)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "include_realm");
+ 			hba_add_jsonb_bool_value(&parseState, true);
+ 		}
+ 
+ 		if (hba->krb_realm)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "krb_realm");
+ 			hba_add_jsonb_string_value(&parseState, hba->krb_realm);
+ 		}
+ 	}
+ 
+ 	if (hba->usermap)
+ 	{
+ 		hba_add_jsonb_string_key(&parseState, "map");
+ 		hba_add_jsonb_string_value(&parseState, hba->usermap);
+ 	}
+ 
+ 	if (hba->clientcert)
+ 	{
+ 		hba_add_jsonb_string_key(&parseState, "clientcert");
+ 		hba_add_jsonb_bool_value(&parseState, true);
+ 	}
+ 
+ 	if (hba->pamservice)
+ 	{
+ 		hba_add_jsonb_string_key(&parseState, "pamservice");
+ 		hba_add_jsonb_string_value(&parseState, hba->pamservice);
+ 	}
+ 
+ 	if (hba->auth_method == uaLDAP)
+ 	{
+ 		if (hba->ldapserver)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapserver");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapserver);
+ 		}
+ 
+ 		if (hba->ldapport)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapport");
+ 			hba_add_jsonb_int32_value(&parseState, hba->ldapport);
+ 		}
+ 
+ 		if (hba->ldaptls)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldaptls");
+ 			hba_add_jsonb_bool_value(&parseState, true);
+ 		}
+ 
+ 		if (hba->ldapprefix)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapprefix");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapprefix);
+ 		}
+ 
+ 		if (hba->ldapsuffix)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapsuffix");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapsuffix);
+ 		}
+ 
+ 		if (hba->ldapbasedn)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapbasedn");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapbasedn);
+ 		}
+ 
+ 		if (hba->ldapbinddn)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapbinddn");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapbinddn);
+ 		}
+ 
+ 		if (hba->ldapbindpasswd)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapbindpasswd");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapbindpasswd);
+ 		}
+ 
+ 		if (hba->ldapsearchattribute)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapsearchattribute");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapsearchattribute);
+ 		}
+ 
+ 		if (hba->ldapscope)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapscope");
+ 			hba_add_jsonb_int32_value(&parseState, hba->ldapscope);
+ 		}
+ 	}
+ 
+ 	if (hba->auth_method == uaRADIUS)
+ 	{
+ 		if (hba->radiusserver)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "radiusserver");
+ 			hba_add_jsonb_string_value(&parseState, hba->radiusserver);
+ 		}
+ 
+ 		if (hba->radiussecret)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "radiussecret");
+ 			hba_add_jsonb_string_value(&parseState, hba->radiussecret);
+ 		}
+ 
+ 		if (hba->radiusidentifier)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "radiusidentifier");
+ 			hba_add_jsonb_string_value(&parseState, hba->radiusidentifier);
+ 		}
+ 
+ 		if (hba->radiusport)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "radiusport");
+ 			hba_add_jsonb_int32_value(&parseState, hba->radiusport);
+ 		}
+ 	}
+ 
+ 	result = pushJsonbValue(&parseState, WJB_END_OBJECT, NULL);
+ 	values[index] = PointerGetDatum(JsonbValueToJsonb(result));
+ 
+ 	/* reason */
+ 	index++;
+ 	if (!hba_match_result)
+ 		values[index] = CStringGetTextDatum(reason);
+ 	else
+ 		nulls[index] = true;
+ }
+ 
+ static bool
+ pg_hba_match(HbaLine *hba, char *database, char *user, struct sockaddr_storage * addr,
+ char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode, char *reason)
+ {
+ 	Oid			roleid;
+ 
+ 	if (ssl_inuse)
+ 	{
+ #ifdef USE_SSL
+ 		if (EnableSSL && hba->conntype != ctHostSSL)
+ 		{
+ 			strcpy(reason, "non SSL connection type");
+ 			return false;
+ 		}
+ 		else
+ 		{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 					 errmsg("hostssl requires SSL to be turned on"),
+ 					 errhint("Set ssl = on in postgresql.conf.")));
+ 			return false;
+ 		}
+ #else
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 				 errmsg("hostssl is not supported by this build"),
+ 			errhint("Compile with --with-openssl to use SSL connections.")));
+ 		return false;
+ #endif
+ 	}
+ 
+ 	/* Get the target role's OID.  Note we do not error out for bad role. */
+ 	roleid = get_role_oid(user, true);
+ 
+ 	if (!check_db(database, user, roleid, hba->databases))
+ 	{
+ 		strcpy(reason, "non matching database");
+ 		return false;
+ 	}
+ 
+ 	if (!check_role(user, roleid, hba->roles))
+ 	{
+ 		strcpy(reason, "non matching role");
+ 		return false;
+ 	}
+ 
+ 	if (args_mode != TWO_ARGS_MODE)
+ 	{
+ 		/* Check IP address */
+ 		switch (hba->ip_cmp_method)
+ 		{
+ 			case ipCmpMask:
+ 				if (hostname && hba->hostname)
+ 				{
+ 					if (!hostname_match(hostname, hba->hostname))
+ 					{
+ 						strcpy(reason, "non matching hostname");
+ 						return false;
+ 					}
+ 				}
+ 				else
+ 				{
+ 					if (!check_ip((SockAddr *) addr,
+ 								  (struct sockaddr *) & hba->addr,
+ 								  (struct sockaddr *) & hba->mask))
+ 					{
+ 						strcpy(reason, "non matching IP");
+ 						return false;
+ 					}
+ 				}
+ 				break;
+ 			case ipCmpAll:
+ 				break;
+ 			case ipCmpSameHost:
+ 			case ipCmpSameNet:
+ 				if (!check_same_host_or_net((SockAddr *) addr,
+ 											hba->ip_cmp_method))
+ 				{
+ 					strcpy(reason, "non matching SameHost/SameNet");
+ 					return false;
+ 				}
+ 				break;
+ 			default:			/* Not reachable case */
+ 				break;
+ 		}
+ 	}
+ 
+ 	return true;
+ }
+ 
+ 
+ #define NUM_PG_HBA_CONF_ATTS   10
+ 
+ static void
+ pg_hba_lookup_internal(char *database, char *user, struct sockaddr_storage * addr,
+ 					   char *hostname, bool ssl_inuse, hba_lookup_args_mode args_mode, ReturnSetInfo *rsi)
+ {
+ 	Tuplestorestate *tuple_store;
+ 	TupleDesc	tupdesc;
+ 	ListCell   *line;
+ 	MemoryContext old_cxt;
+ 
+ 	/*
+ 	 * Create the tupledesc and tuplestore in the per_query context as
+ 	 * required for SFRM_Materialize.
+ 	 */
+ 	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+ 
+ 	tupdesc = CreateTemplateTupleDesc(NUM_PG_HBA_CONF_ATTS, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "line_number",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "mode",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "type",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "database",
+ 					   JSONBOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "user",
+ 					   JSONBOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "address",
+ 					   INETOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "hostname",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "method",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "options",
+ 					   JSONBOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "reason",
+ 					   TEXTOID, -1, 0);
+ 	BlessTupleDesc(tupdesc);
+ 
+ 	tuple_store =
+ 		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+ 							  false, work_mem);
+ 
+ 	MemoryContextSwitchTo(old_cxt);
+ 
+ 	/*
+ 	 * Loop through the list and deparse each entry as it comes, storing it in
+ 	 * the tuplestore. Any temporary memory allocations here live only for the
+ 	 * function call lifetime.
+ 	 */
+ 	foreach(line, parsed_hba_lines)
+ 	{
+ 		HbaLine    *hba = (HbaLine *) lfirst(line);
+ 		Datum		values[NUM_PG_HBA_CONF_ATTS];
+ 		bool		nulls[NUM_PG_HBA_CONF_ATTS];
+ 		HeapTuple	tuple;
+ 		bool		hba_match_result;
+ 		char		reason[MAX_LEN_OF_NON_MATCH_HBA_ENTRY_REASON];
+ 
+ 		memset(values, 0, sizeof(values));
+ 		memset(nulls, 0, sizeof(nulls));
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		hba_match_result = pg_hba_match(hba, database, user, addr, hostname,
+ 										ssl_inuse, args_mode, reason);
+ 
+ 		/* Get the next parsed hba line values */
+ 		hba_getvalues_for_line(hba, values, nulls, hba_match_result, reason);
+ 
+ 		/* build a tuple */
+ 		tuple = heap_form_tuple(tupdesc, values, nulls);
+ 		tuplestore_puttuple(tuple_store, tuple);
+ 
+ 		/* Stop the loop processing, if matching entry is found */
+ 		if (hba_match_result)
+ 			break;
+ 	}
+ 
+ 	rsi->setDesc = tupdesc;
+ 	rsi->setResult = tuple_store;
+ 	return;
+ }
+ 
+ /*
+ * SQL-accessible SRF to return all the settings from the pg_hba.conf
+ * file.
+ */
+ Datum
+ pg_hba_lookup_2args(PG_FUNCTION_ARGS)
+ {
+ 	return pg_hba_lookup(fcinfo);
+ }
+ 
+ /*
+  * SQL-accessible SRF to return all the settings from the pg_hba.conf
+  * file.
+  */
+ Datum
+ pg_hba_lookup_3args(PG_FUNCTION_ARGS)
+ {
+ 	return pg_hba_lookup(fcinfo);
+ }
+ 
+ /*
+  * SQL-accessible SRF to return all the settings from the pg_hba.conf
+  * file.
+  */
+ Datum
+ pg_hba_lookup(PG_FUNCTION_ARGS)
+ {
+ 	char	   *user;
+ 	char	   *database;
+ 	char	   *address;
+ 	char	   *hostname;
+ 	bool		ssl_inuse = false;
+ 	struct sockaddr_storage addr;
+ 	hba_lookup_args_mode args_mode = TWO_ARGS_MODE;		/* Minimum number of
+ 														 * arguments */
+ 
+ 	/*
+ 	 * 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.
+ 	 */
+ 	ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("must be superuser to view pg_hba.conf settings"))));
+ 
+ 	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+ 		(rsi->allowedModes & SFRM_Materialize) == 0)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("set-valued function called in context that "
+ 						"cannot accept a set")));
+ 
+ 	if (PG_ARGISNULL(0))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 				 (errmsg("database name is required to match pg_hba configuration entry"))));
+ 	else
+ 		database = TextDatumGetCString(PG_GETARG_DATUM(0));
+ 
+ 	if (PG_ARGISNULL(1))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 				 (errmsg("user name is required to match pg_hba configuration entry"))));
+ 	else
+ 		user = TextDatumGetCString(PG_GETARG_DATUM(1));
+ 
+ 	if (PG_NARGS() != 2)
+ 	{
+ 		struct addrinfo *gai_result;
+ 		struct addrinfo hints;
+ 		int			ret;
+ 
+ 		args_mode = THREE_ARGS_MODE;
+ 		if (!PG_ARGISNULL(2))
+ 			address = TextDatumGetCString(PG_GETARG_DATUM(2));
+ 
+ 		/* Get the IP address either way */
+ 		hints.ai_flags = AI_NUMERICHOST;
+ 		hints.ai_family = AF_UNSPEC;
+ 		hints.ai_socktype = 0;
+ 		hints.ai_protocol = 0;
+ 		hints.ai_addrlen = 0;
+ 		hints.ai_canonname = NULL;
+ 		hints.ai_addr = NULL;
+ 		hints.ai_next = NULL;
+ 
+ 		ret = pg_getaddrinfo_all(address, NULL, &hints, &gai_result);
+ 		if (ret == 0 && gai_result)
+ 			memcpy(&addr, gai_result->ai_addr, gai_result->ai_addrlen);
+ 		else if (ret == EAI_NONAME)
+ 			hostname = pstrdup(address);
+ 		else
+ 		{
+ 			ereport(ERROR,
+ 					(errmsg("invalid IP address \"%s\": %s",
+ 							address, gai_strerror(ret))));
+ 			if (gai_result)
+ 				pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ 			return false;
+ 		}
+ 
+ 		pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ 
+ 		if (PG_NARGS() != 3)
+ 		{
+ 			args_mode = FOUR_ARGS_MODE;
+ 			if (!PG_ARGISNULL(3))
+ 				ssl_inuse = DatumGetBool(PG_GETARG_DATUM(3));
+ 		}
+ 	}
+ 
+ 	if (load_hba_failure)
+ 		ereport(WARNING,
+ 			 (errmsg("There was some failure in reloading pg_hba.conf file. "
+ 		   "The pg_hba.conf settings data may contains stale information")));
+ 
+ 	rsi->returnMode = SFRM_Materialize;
+ 
+ 	pg_hba_lookup_internal(database, user, &addr, hostname, ssl_inuse, args_mode, rsi);
+ 
+ 	PG_RETURN_NULL();
+ }
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 4005,4010 **** PostgresMain(int argc, char *argv[],
--- 4005,4022 ----
  		{
  			got_SIGHUP = false;
  			ProcessConfigFile(PGC_SIGHUP);
+ 
+ 			/* 
+ 			 * Reload authentication config files too to refresh 
+ 			 * pg_hba_conf view data.
+ 			 */
+ 			load_hba_failure = false;
+ 			if (!load_hba())
+ 			{
+ 				ereport(DEBUG1,
+ 					(errmsg("Falure in reloading pg_hba.conf, pg_hba_conf view may show stale information")));
+ 				load_hba_failure = true;
+ 			}
  		}
  
  		/*
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3079,3084 **** DATA(insert OID = 2084 (  pg_show_all_settings	PGNSP PGUID 12 1 1000 0 0 f f f f
--- 3079,3090 ----
  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 = 3997 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v u 2 0 2249 "25 25" "{25,25,23,25,25,3802,3802,869,25,25,3802,25}" "{i,i,o,o,o,o,o,o,o,o,o,o}" "{database,user,line_number,mode,type,database,user,address,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup_2args _null_ _null_ _null_));
+ DESCR("view client authentication settings");
+ DATA(insert OID = 3998 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v u 3 0 2249 "25 25 25" "{25,25,25,23,25,25,3802,3802,869,25,25,3802,25}" "{i,i,i,o,o,o,o,o,o,o,o,o,o}" "{database,user,address,line_number,mode,type,database,user,address,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup_3args _null_ _null_ _null_));
+ DESCR("view client authentication settings");
+ DATA(insert OID = 3999 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f t t v u 4 0 2249 "25 25 25 16" "{25,25,25,16,23,25,25,3802,3802,869,25,25,3802,25}" "{i,i,i,i,o,o,o,o,o,o,o,o,o,o}" "{database,user,address,ssl_inuse,line_number,mode,type,database,user,address,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup _null_ _null_ _null_));
+ DESCR("view client authentication settings");
  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 = 1065 (  pg_prepared_xact PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{28,25,1184,26,26}" "{o,o,o,o,o}" "{transaction,gid,prepared,ownerid,dbid}" _null_ _null_ pg_prepared_xact _null_ _null_ _null_ ));
*** a/src/include/libpq/hba.h
--- b/src/include/libpq/hba.h
***************
*** 96,101 **** typedef struct IdentLine
--- 96,103 ----
  /* kluge to avoid including libpq/libpq-be.h here */
  typedef struct Port hbaPort;
  
+ extern bool load_hba_failure;
+ 
  extern bool load_hba(void);
  extern bool load_ident(void);
  extern void hba_getauthmethod(hbaPort *port);
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 1123,1128 **** extern Datum set_config_by_name(PG_FUNCTION_ARGS);
--- 1123,1133 ----
  extern Datum show_all_settings(PG_FUNCTION_ARGS);
  extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
  
+ /* hba.c */
+ extern Datum pg_hba_lookup_2args(PG_FUNCTION_ARGS);
+ extern Datum pg_hba_lookup_3args(PG_FUNCTION_ARGS);
+ extern Datum pg_hba_lookup(PG_FUNCTION_ARGS);
+ 
  /* rls.c */
  extern Datum row_security_active(PG_FUNCTION_ARGS);
  extern Datum row_security_active_name(PG_FUNCTION_ARGS);
#27Amit Kapila
amit.kapila16@gmail.com
In reply to: Haribabu Kommi (#26)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Wed, Dec 9, 2015 at 6:48 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Wed, Dec 9, 2015 at 2:36 AM, Amit Kapila <amit.kapila16@gmail.com>

wrote:

I think it is better to have check on number of args in the
above functions similar to what we have in ginarrayextract_2args.

ginarrayextract_2args is an deprecated function that checks and returns
error if user is using with two arguments. But in pg_hba_lookup function,
providing two argument is a valid scenario. The check can be added only
to verify whether the provided number of arguments are two or not. Is it
really required?

I think we can live without such a check especially because you
already have required check for function args in pg_hba_lookup
function.

2.
+
+ /*
+ * Reload authentication config files too to refresh
+ * pg_hba_conf view data.
+ */
+ if (!load_hba())
+ {
+ ereport(DEBUG1,
+ (errmsg("Falure in reloading pg_hba.conf, pg_hba_conf view may show

stale

information")));
+ load_hba_failure = true;
+ }
+
+ load_hba_failure = false;

Won't the above code set load_hba_failure as false even in
failure case.

Fixed.

Another bigger issue I see in the above part of code is that it doesn't
seem to be safe to call load_hba() at that place in PostgresMain() as
currently load_hba() is using a context created from PostmasterContext
to perform the parsing and some other stuff, the PostmasterContext
won't be available at that time. It is deleted immediately after
InitPostgres
is completed. So either we need to make PostmasterContext don't go
away after InitPostgres() or load_hba shouldn't use it and rather use
CurrentMemroyContext similar to ProcessConfigFile or may be use
TopMemoryContext instead of PostmasterContext if possible. I think
this needs some more thoughts.

Apart from above, this patch doesn't seem to work on Windows or
for EXEC_BACKEND builds as we are loading the hba file in a
temporary context (PostmasterContext, refer PerformAuthentication)
which won't be alive for the backends life. This works on linux because
of fork/exec mechanism which allows to inherit the parsed file
(parsed_hba_lines). This is slightly tricky problem to solve and we
have couple of options (a) use TopMemoryContext instead of Postmaster
Context to load hba; (b) Use CurrentMemoryContext (c) pass the parsed
hba file for Windows/Exec_Backend using save_backend_variables/
restore_backend_variables mechanism or if you have any other idea.
If you don't have any better idea, then you can evaluate above ideas
and see which one makes more sense.

With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#28Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Amit Kapila (#27)
1 attachment(s)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Wed, Dec 9, 2015 at 5:31 PM, Amit Kapila <amit.kapila16@gmail.com> wrote:

Another bigger issue I see in the above part of code is that it doesn't
seem to be safe to call load_hba() at that place in PostgresMain() as
currently load_hba() is using a context created from PostmasterContext
to perform the parsing and some other stuff, the PostmasterContext
won't be available at that time. It is deleted immediately after
InitPostgres
is completed. So either we need to make PostmasterContext don't go
away after InitPostgres() or load_hba shouldn't use it and rather use
CurrentMemroyContext similar to ProcessConfigFile or may be use
TopMemoryContext instead of PostmasterContext if possible. I think
this needs some more thoughts.

Apart from above, this patch doesn't seem to work on Windows or
for EXEC_BACKEND builds as we are loading the hba file in a
temporary context (PostmasterContext, refer PerformAuthentication)
which won't be alive for the backends life. This works on linux because
of fork/exec mechanism which allows to inherit the parsed file
(parsed_hba_lines). This is slightly tricky problem to solve and we
have couple of options (a) use TopMemoryContext instead of Postmaster
Context to load hba; (b) Use CurrentMemoryContext (c) pass the parsed
hba file for Windows/Exec_Backend using save_backend_variables/
restore_backend_variables mechanism or if you have any other idea.
If you don't have any better idea, then you can evaluate above ideas
and see which one makes more sense.

Reverting the context release patch is already provided in the first
mail of this
thread [1]/messages/by-id/CAJrrPGfFyf45mfK7uB+QHWhXn_tTMkNRVhTuDefQzuZZRwEeQg@mail.gmail.com. Forgot to mention about the same in further mails.

Here I attached the same patch. This patch needs to be applied first before
pg_hba_lookup patch. I tested it in windows version also.

[1]: /messages/by-id/CAJrrPGfFyf45mfK7uB+QHWhXn_tTMkNRVhTuDefQzuZZRwEeQg@mail.gmail.com

Regards,
Hari Babu
Fujitsu Australia

Attachments:

revert_hba_context_release_in_backend.patchapplication/octet-stream; name=revert_hba_context_release_in_backend.patchDownload
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 386,392 **** tokenize_file(const char *filename, FILE *file,
  	MemoryContext linecxt;
  	MemoryContext oldcxt;
  
! 	linecxt = AllocSetContextCreate(CurrentMemoryContext,
  									"tokenize file cxt",
  									ALLOCSET_DEFAULT_MINSIZE,
  									ALLOCSET_DEFAULT_INITSIZE,
--- 386,392 ----
  	MemoryContext linecxt;
  	MemoryContext oldcxt;
  
! 	linecxt = AllocSetContextCreate(TopMemoryContext,
  									"tokenize file cxt",
  									ALLOCSET_DEFAULT_MINSIZE,
  									ALLOCSET_DEFAULT_INITSIZE,
***************
*** 1770,1777 **** load_hba(void)
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	Assert(PostmasterContext);
! 	hbacxt = AllocSetContextCreate(PostmasterContext,
  								   "hba parser context",
  								   ALLOCSET_DEFAULT_MINSIZE,
  								   ALLOCSET_DEFAULT_MINSIZE,
--- 1770,1776 ----
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	hbacxt = AllocSetContextCreate(TopMemoryContext,
  								   "hba parser context",
  								   ALLOCSET_DEFAULT_MINSIZE,
  								   ALLOCSET_DEFAULT_MINSIZE,
***************
*** 2148,2155 **** load_ident(void)
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	Assert(PostmasterContext);
! 	ident_context = AllocSetContextCreate(PostmasterContext,
  										  "ident parser context",
  										  ALLOCSET_DEFAULT_MINSIZE,
  										  ALLOCSET_DEFAULT_MINSIZE,
--- 2147,2153 ----
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	ident_context = AllocSetContextCreate(TopMemoryContext,
  										  "ident parser context",
  										  ALLOCSET_DEFAULT_MINSIZE,
  										  ALLOCSET_DEFAULT_MINSIZE,
*** a/src/backend/utils/init/postinit.c
--- b/src/backend/utils/init/postinit.c
***************
*** 52,58 ****
  #include "utils/acl.h"
  #include "utils/fmgroids.h"
  #include "utils/guc.h"
- #include "utils/memutils.h"
  #include "utils/pg_locale.h"
  #include "utils/portal.h"
  #include "utils/ps_status.h"
--- 52,57 ----
***************
*** 191,208 **** PerformAuthentication(Port *port)
  	 * FIXME: [fork/exec] Ugh.  Is there a way around this overhead?
  	 */
  #ifdef EXEC_BACKEND
- 	/*
- 	 * load_hba() and load_ident() want to work within the PostmasterContext,
- 	 * so create that if it doesn't exist (which it won't).  We'll delete it
- 	 * again later, in PostgresMain.
- 	 */
- 	if (PostmasterContext == NULL)
- 		PostmasterContext = AllocSetContextCreate(TopMemoryContext,
- 												  "Postmaster",
- 												  ALLOCSET_DEFAULT_MINSIZE,
- 												  ALLOCSET_DEFAULT_INITSIZE,
- 												  ALLOCSET_DEFAULT_MAXSIZE);
- 
  	if (!load_hba())
  	{
  		/*
--- 190,195 ----
#29Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Haribabu Kommi (#28)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

Haribabu Kommi wrote:

Reverting the context release patch is already provided in the first
mail of this
thread [1]. Forgot to mention about the same in further mails.

Here I attached the same patch. This patch needs to be applied first before
pg_hba_lookup patch. I tested it in windows version also.

So if you change the file and reload repeatedly, we leak all the memory
allocated for HBA lines in TopMemoryContext? This doesn't sound great.
Perhaps we need a dedicated context which can be reset at will so that
it can be refilled with the right info when we reload the file.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#30Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Alvaro Herrera (#29)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Thu, Dec 10, 2015 at 4:22 AM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

Haribabu Kommi wrote:

Reverting the context release patch is already provided in the first
mail of this
thread [1]. Forgot to mention about the same in further mails.

Here I attached the same patch. This patch needs to be applied first before
pg_hba_lookup patch. I tested it in windows version also.

So if you change the file and reload repeatedly, we leak all the memory
allocated for HBA lines in TopMemoryContext? This doesn't sound great.
Perhaps we need a dedicated context which can be reset at will so that
it can be refilled with the right info when we reload the file.

No. There is no leaks associated with pg_hba.conf parsing. we already have
a memory context called "hba parser context" allocated from Postmaster
context. The "revert_hba_context_release_in_backend" patch changes it to
TopMemoryContext. The memory required for parsing and storing parsed
hba lines is obtained from this context.

Regards,
Hari Babu
Fujitsu Australia

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#31Amit Kapila
amit.kapila16@gmail.com
In reply to: Haribabu Kommi (#30)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Thu, Dec 10, 2015 at 6:46 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Thu, Dec 10, 2015 at 4:22 AM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

Haribabu Kommi wrote:

Reverting the context release patch is already provided in the first
mail of this
thread [1]. Forgot to mention about the same in further mails.

Here I attached the same patch. This patch needs to be applied first

before

pg_hba_lookup patch. I tested it in windows version also.

So if you change the file and reload repeatedly, we leak all the memory
allocated for HBA lines in TopMemoryContext? This doesn't sound great.
Perhaps we need a dedicated context which can be reset at will so that
it can be refilled with the right info when we reload the file.

No. There is no leaks associated with pg_hba.conf parsing. we already have
a memory context called "hba parser context" allocated from Postmaster
context. The "revert_hba_context_release_in_backend" patch changes it to
TopMemoryContext. The memory required for parsing and storing parsed
hba lines is obtained from this context.

tokenize_file() is called before creation of hba parser context, so below
change would be problem.

*** 386,392 **** tokenize_file(const char *filename, FILE *file,

MemoryContext linecxt;

MemoryContext oldcxt;

! linecxt = AllocSetContextCreate(CurrentMemoryContext,

"tokenize file cxt",

ALLOCSET_DEFAULT_MINSIZE,

ALLOCSET_DEFAULT_INITSIZE,

--- 386,392 ----

MemoryContext linecxt;

MemoryContext oldcxt;

! linecxt = AllocSetContextCreate(TopMemoryContext,

"tokenize file cxt",

ALLOCSET_DEFAULT_MINSIZE,

ALLOCSET_DEFAULT_INITSIZE,

How about creating "hba parser context" and "ident parser context"
at the beginning of their respective functions and don't change
anything in tokenize_file()?

With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#32Amit Kapila
amit.kapila16@gmail.com
In reply to: Haribabu Kommi (#28)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Wed, Dec 9, 2015 at 2:35 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

Reverting the context release patch is already provided in the first
mail of this
thread [1]. Forgot to mention about the same in further mails.

Thanks, that is helpful. However I think it is better if you can
always keep the link of related patches at end of mail.

With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#33Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Amit Kapila (#31)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Thu, Dec 10, 2015 at 2:29 PM, Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Dec 10, 2015 at 6:46 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Thu, Dec 10, 2015 at 4:22 AM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

Haribabu Kommi wrote:

Reverting the context release patch is already provided in the first
mail of this
thread [1]. Forgot to mention about the same in further mails.

Here I attached the same patch. This patch needs to be applied first
before
pg_hba_lookup patch. I tested it in windows version also.

So if you change the file and reload repeatedly, we leak all the memory
allocated for HBA lines in TopMemoryContext? This doesn't sound great.
Perhaps we need a dedicated context which can be reset at will so that
it can be refilled with the right info when we reload the file.

No. There is no leaks associated with pg_hba.conf parsing. we already have
a memory context called "hba parser context" allocated from Postmaster
context. The "revert_hba_context_release_in_backend" patch changes it to
TopMemoryContext. The memory required for parsing and storing parsed
hba lines is obtained from this context.

tokenize_file() is called before creation of hba parser context, so below
change would be problem.

*** 386,392 **** tokenize_file(const char *filename, FILE *file,

MemoryContext linecxt;

MemoryContext oldcxt;

! linecxt = AllocSetContextCreate(CurrentMemoryContext,

"tokenize file cxt",

ALLOCSET_DEFAULT_MINSIZE,

ALLOCSET_DEFAULT_INITSIZE,

--- 386,392 ----

MemoryContext linecxt;

MemoryContext oldcxt;

! linecxt = AllocSetContextCreate(TopMemoryContext,

"tokenize file cxt",

ALLOCSET_DEFAULT_MINSIZE,

ALLOCSET_DEFAULT_INITSIZE,

How about creating "hba parser context" and "ident parser context"
at the beginning of their respective functions and don't change
anything in tokenize_file()?

The tokenize file cxt is deleted after a successful load of pg_hba.conf or
pg_ident.conf files. we don't need this memory once the pg_hba.conf
or pg_ident file is loaded, because of this reason, it is created as a
separate context and deleted later.

Regards,
Hari Babu
Fujitsu Australia

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#34Amit Kapila
amit.kapila16@gmail.com
In reply to: Haribabu Kommi (#33)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Thu, Dec 10, 2015 at 9:51 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Thu, Dec 10, 2015 at 2:29 PM, Amit Kapila <amit.kapila16@gmail.com>

wrote:

How about creating "hba parser context" and "ident parser context"
at the beginning of their respective functions and don't change
anything in tokenize_file()?

The tokenize file cxt is deleted after a successful load of pg_hba.conf or
pg_ident.conf files. we don't need this memory once the pg_hba.conf
or pg_ident file is loaded, because of this reason, it is created as a
separate context and deleted later.

What about the error case?

With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

#35Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Amit Kapila (#34)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Thu, Dec 10, 2015 at 4:33 PM, Amit Kapila <amit.kapila16@gmail.com> wrote:

On Thu, Dec 10, 2015 at 9:51 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Thu, Dec 10, 2015 at 2:29 PM, Amit Kapila <amit.kapila16@gmail.com>
wrote:

How about creating "hba parser context" and "ident parser context"
at the beginning of their respective functions and don't change
anything in tokenize_file()?

The tokenize file cxt is deleted after a successful load of pg_hba.conf or
pg_ident.conf files. we don't need this memory once the pg_hba.conf
or pg_ident file is loaded, because of this reason, it is created as a
separate context and deleted later.

What about the error case?

Yes, One error case is possible when the length of the string crosses
the MAX_LINE size.
If we allocate the tokenize file cxt inside CurrentMemoryContext (i.e
MessageContext)
instead of TopMemoryContext, it will automatically freed later in case
if exists.

Regards,
Hari Babu
Fujitsu Australia

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#36Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Haribabu Kommi (#35)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

Hi,

I've reviewed the patch today, after re-reading the whole discussion.

Overall I'm quite happy with the design - a function is certainly better
for the use-case. Not just because of the keyword handling issues, but
because the checks happen in a particular order and terminate once a
match is found, and that's very difficult to do with a view. So +1 to
the function approach. Also +1 to abandoning the NOTICE idea and just
generating rows.

The one unsolved issue is what to do with 1e24cf64. My understanding is
that the current patch still requires reverting of that patch, but my
feeling is TL won't be particularly keen about doing that. Or am I
missing something?

The current patch (v6) also triggers a few warnings during compilation,
about hostname/address being unitialized in pg_hba_lookup(). That
happens because 'address' is only set when (! PG_ARGISNULL(2)). Fixing
it is as simple as

char *address = NULL;
char *hostname = NULL;

at the beginning of the function (this seems correct to me).

The current patch also does not handle 'all' keywords correctly - it
apparently just compares the values as strings, which is incorrect. For
example this

SELECT * FROM pg_hba_lookup('all', 'all')

matches this pg_hba.conf line

local all all trust

That's clearly incorrect, as Alvaro pointed out.

I'm also wondering whether we really need three separate functions in
pg_proc.

pg_hba_lookup(database, user)
pg_hba_lookup(database, user, address)
pg_hba_lookup(database, user, address, ssl_inuse)

Clearly, that's designed to match the local/host/hostssl/hostnossl cases
available in pg_hba. But why not to simply use default values instead?

pg_hba_lookup(database TEXT, user TEXT,
address TEXT DEFAULT NULL,
ssl_inuse BOOLEAN DEFAULT NULL)

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#37Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Tomas Vondra (#36)
2 attachment(s)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Wed, Dec 16, 2015 at 8:19 AM, Tomas Vondra
<tomas.vondra@2ndquadrant.com> wrote:

Hi,

I've reviewed the patch today, after re-reading the whole discussion.

Thanks for the review.

The one unsolved issue is what to do with 1e24cf64. My understanding is that
the current patch still requires reverting of that patch, but my feeling is
TL won't be particularly keen about doing that. Or am I missing something?

Until pg_hba_lookup function, the parsed hba lines are not used in the backend.
These are used only postmaster process for the authentication. As the parsed
hba lines occupy extra memory in the backend process which is of no use.
Because of this reason TL has changed it to PostmasterContext instead of
TopMemoryContext.

The current patch (v6) also triggers a few warnings during compilation,
about hostname/address being unitialized in pg_hba_lookup(). That happens
because 'address' is only set when (! PG_ARGISNULL(2)). Fixing it is as
simple as

char *address = NULL;
char *hostname = NULL;

at the beginning of the function (this seems correct to me).

corrected.

The current patch also does not handle 'all' keywords correctly - it
apparently just compares the values as strings, which is incorrect. For
example this

SELECT * FROM pg_hba_lookup('all', 'all')

matches this pg_hba.conf line

local all all trust

That's clearly incorrect, as Alvaro pointed out.

In the above case, the 'all' is taken as a database and user names.
The pg_hba line contains the keyword of 'all' as database and user.
This line can match with any database and user names provided
by the user. Because of this reason, it matches with the first line
of pg_hba.conf.

I feel it is fine. Please let me know if you are expecting a different
behavior.

I'm also wondering whether we really need three separate functions in
pg_proc.

pg_hba_lookup(database, user)
pg_hba_lookup(database, user, address)
pg_hba_lookup(database, user, address, ssl_inuse)

Clearly, that's designed to match the local/host/hostssl/hostnossl cases
available in pg_hba. But why not to simply use default values instead?

pg_hba_lookup(database TEXT, user TEXT,
address TEXT DEFAULT NULL,
ssl_inuse BOOLEAN DEFAULT NULL)

Function is changed to accept default values.

Apart from the above, added a local memory context to allocate the memory
required for forming tuple for each line. This context resets for every hba line
to avoid consuming unnecessary memory for scenarios of huge pg_hba.conf
files.

In the revert_hba_context_release_in_backend patch, apart from reverting
the commit - 1e24cf64. In tokenize_file function, changed the new context
allocation from CurrentMemoryContext instead of TopMemoryContext.

Patch apply process:
1. revert_hba_context_release_in_backend_2.patch
2. pg_hba_lookup_poc_v7.patch

Regards,
Hari Babu
Fujitsu Australia

Attachments:

revert_hba_context_release_in_backend_2.patchapplication/octet-stream; name=revert_hba_context_release_in_backend_2.patchDownload
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 1770,1777 **** load_hba(void)
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	Assert(PostmasterContext);
! 	hbacxt = AllocSetContextCreate(PostmasterContext,
  								   "hba parser context",
  								   ALLOCSET_DEFAULT_MINSIZE,
  								   ALLOCSET_DEFAULT_MINSIZE,
--- 1770,1776 ----
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	hbacxt = AllocSetContextCreate(TopMemoryContext,
  								   "hba parser context",
  								   ALLOCSET_DEFAULT_MINSIZE,
  								   ALLOCSET_DEFAULT_MINSIZE,
***************
*** 2148,2155 **** load_ident(void)
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	Assert(PostmasterContext);
! 	ident_context = AllocSetContextCreate(PostmasterContext,
  										  "ident parser context",
  										  ALLOCSET_DEFAULT_MINSIZE,
  										  ALLOCSET_DEFAULT_MINSIZE,
--- 2147,2153 ----
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	ident_context = AllocSetContextCreate(TopMemoryContext,
  										  "ident parser context",
  										  ALLOCSET_DEFAULT_MINSIZE,
  										  ALLOCSET_DEFAULT_MINSIZE,
*** a/src/backend/utils/init/postinit.c
--- b/src/backend/utils/init/postinit.c
***************
*** 52,58 ****
  #include "utils/acl.h"
  #include "utils/fmgroids.h"
  #include "utils/guc.h"
- #include "utils/memutils.h"
  #include "utils/pg_locale.h"
  #include "utils/portal.h"
  #include "utils/ps_status.h"
--- 52,57 ----
***************
*** 191,208 **** PerformAuthentication(Port *port)
  	 * FIXME: [fork/exec] Ugh.  Is there a way around this overhead?
  	 */
  #ifdef EXEC_BACKEND
- 	/*
- 	 * load_hba() and load_ident() want to work within the PostmasterContext,
- 	 * so create that if it doesn't exist (which it won't).  We'll delete it
- 	 * again later, in PostgresMain.
- 	 */
- 	if (PostmasterContext == NULL)
- 		PostmasterContext = AllocSetContextCreate(TopMemoryContext,
- 												  "Postmaster",
- 												  ALLOCSET_DEFAULT_MINSIZE,
- 												  ALLOCSET_DEFAULT_INITSIZE,
- 												  ALLOCSET_DEFAULT_MAXSIZE);
- 
  	if (!load_hba())
  	{
  		/*
--- 190,195 ----
pg_hba_lookup_poc_v7.patchapplication/octet-stream; name=pg_hba_lookup_poc_v7.patchDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 16576,16581 **** SELECT collation for ('foo' COLLATE "de_DE");
--- 16576,16594 ----
         <entry><type>text</type></entry>
         <entry>set parameter and return new value</entry>
        </row>
+       <row>
+        <entry>
+         <indexterm>
+          <primary>pg_hba_lookup</primary>
+         </indexterm>
+         <literal><function>pg_hba_lookup(<parameter>database</> <type>text</>,
+                             <parameter>user_name</> <type>text</>
+                             [, <parameter>address</> <type>text</>]
+                             [, <parameter>ssl_inuse</> <type>text</>)</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>Returns all row entries of pg_hba.conf till matching entry is found</entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
***************
*** 16633,16638 **** SELECT set_config('log_statement_stats', 'off', false);
--- 16646,16668 ----
  </programlisting>
     </para>
  
+    <para>
+     <function>pg_hba_lookup</function> returns a set of records
+     containing the line number, mode, type, database, user_name,
+     address, hostname, method, options and reason. Typical usages
+     include:
+ <programlisting>
+ postgres=# select * from pg_hba_lookup('postgres','kommih', '::1');
+  line_number |  mode   | type  | database | user_name |  address  | hostname | method | options |     reason      
+ -------------+---------+-------+----------+-----------+-----------+----------+--------+---------+-----------------
+           84 | skipped | local | ["all"]  | ["all"]   |           |          | trust  | {}      | non matching IP
+           86 | skipped | host  | ["all"]  | ["all"]   | 127.0.0.1 |          | trust  | {}      | non matching IP
+           88 | matched | host  | ["all"]  | ["all"]   | ::1       |          | trust  | {}      | 
+ (3 rows)
+ </programlisting>
+     Only super user can access this function to view the pg_hba entries.
+    </para>
+    
    </sect2>
  
    <sect2 id="functions-admin-signal">
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 940,942 **** RETURNS jsonb
--- 940,951 ----
  LANGUAGE INTERNAL
  STRICT IMMUTABLE
  AS 'jsonb_set';
+ 
+ CREATE OR REPLACE FUNCTION pg_hba_lookup(IN database text, IN user_name text,
+     IN address text default NULL, IN ssl_inuse boolean default false,
+ 	OUT line_number int, OUT mode text, OUT type text, OUT database jsonb,
+ 	OUT user_name jsonb, OUT address inet,	OUT hostname text, OUT method text,
+ 	OUT options jsonb, OUT reason text)
+ RETURNS SETOF RECORD
+ LANGUAGE INTERNAL
+ AS 'pg_hba_lookup';
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 25,39 ****
--- 25,45 ----
  #include <arpa/inet.h>
  #include <unistd.h>
  
+ #include "access/htup_details.h"
  #include "catalog/pg_collation.h"
+ #include "catalog/pg_type.h"
+ #include "funcapi.h"
  #include "libpq/ip.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/jsonb.h"
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  
***************
*** 52,57 ****
--- 58,65 ----
  #define MAX_TOKEN	256
  #define MAX_LINE	8192
  
+ #define MAX_LEN_OF_NON_MATCH_HBA_ENTRY_REASON 256
+ 
  /* callback data for check_network_callback */
  typedef struct check_network_data
  {
***************
*** 74,79 **** typedef struct HbaToken
--- 82,90 ----
  	bool		quoted;
  } HbaToken;
  
+ /* Flag to indicate the failure of reloading pg_hba.conf file */
+ bool		load_hba_failure = false;
+ 
  /*
   * pre-parsed content of HBA config file: list of HbaLine structs.
   * parsed_hba_context is the memory context where it lives.
***************
*** 99,104 **** static List *tokenize_inc_file(List *tokens, const char *outer_filename,
--- 110,127 ----
  				  const char *inc_filename);
  static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
  				   int line_num);
+ static Datum getauthmethod(UserAuth auth_method);
+ static void hba_add_jsonb_string_elem(JsonbParseState **pstate, char *string_elem);
+ static void hba_add_jsonb_string_key(JsonbParseState **pstate, char *string_key);
+ static void hba_add_jsonb_bool_value(JsonbParseState **pstate, bool bool_val);
+ static void hba_add_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val);
+ static void hba_add_jsonb_string_value(JsonbParseState **pstate, char *string_value);
+ static void hba_getvalues_for_line(HbaLine *hba, Datum *values, bool *nulls,
+ 					   bool hba_match_result, char *reason);
+ static bool pg_hba_match(HbaLine *hba, char *user, char *database, struct sockaddr_storage * addr,
+ 			 char *hostname, bool ssl_inuse, char *reason);
+ static void pg_hba_lookup_internal(char *database, char *user, struct sockaddr_storage * addr,
+ 					   char *hostname, bool ssl_inuse, ReturnSetInfo *rsi);
  
  /*
   * isblank() exists in the ISO C99 spec, but it's not very portable yet,
***************
*** 2233,2235 **** hba_getauthmethod(hbaPort *port)
--- 2256,2900 ----
  {
  	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 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 uaLDAP:
+ 			result = CStringGetTextDatum("ldap");
+ 			break;
+ 		case uaCert:
+ 			result = CStringGetTextDatum("cert");
+ 			break;
+ 		case uaRADIUS:
+ 			result = CStringGetTextDatum("radius");
+ 			break;
+ 		case uaPeer:
+ 			result = CStringGetTextDatum("peer");
+ 			break;
+ 		default:
+ 			elog(ERROR, "unexpected authentication method in parsed HBA entry");
+ 			break;
+ 	}
+ 
+ 	return result;
+ }
+ 
+ static void
+ hba_add_jsonb_string_elem(JsonbParseState **pstate, char *string_elem)
+ {
+ 	JsonbValue	jb;
+ 
+ 	jb.type = jbvString;
+ 	jb.val.string.len = strlen(string_elem);
+ 	jb.val.string.val = pstrdup(string_elem);
+ 	pushJsonbValue(pstate, WJB_ELEM, &jb);
+ }
+ 
+ static void
+ hba_add_jsonb_string_key(JsonbParseState **pstate, char *string_key)
+ {
+ 	JsonbValue	jb;
+ 
+ 	jb.type = jbvString;
+ 	jb.val.string.len = strlen(string_key);
+ 	jb.val.string.val = pstrdup(string_key);
+ 	pushJsonbValue(pstate, WJB_KEY, &jb);
+ }
+ 
+ static void
+ hba_add_jsonb_bool_value(JsonbParseState **pstate, bool bool_val)
+ {
+ 	JsonbValue	jb;
+ 
+ 	jb.type = jbvBool;
+ 	jb.val.boolean = bool_val;
+ 
+ 	pushJsonbValue(pstate, WJB_VALUE, &jb);
+ }
+ 
+ static void
+ hba_add_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val)
+ {
+ 	JsonbValue	jb;
+ 	char		outputstr[64];
+ 
+ 	sprintf(outputstr, "%d", int32_val);
+ 	jb.type = jbvNumeric;
+ 	jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1));
+ 
+ 	pushJsonbValue(pstate, WJB_VALUE, &jb);
+ }
+ 
+ static void
+ hba_add_jsonb_string_value(JsonbParseState **pstate, char *string_value)
+ {
+ 	JsonbValue	jb;
+ 
+ 	jb.type = jbvString;
+ 	jb.val.string.len = strlen(string_value);
+ 	jb.val.string.val = pstrdup(string_value);
+ 	pushJsonbValue(pstate, WJB_VALUE, &jb);
+ }
+ 
+ /*
+  * Fill in suitable values to build a tuple representing the
+  * HbaLine provided
+  */
+ static void
+ hba_getvalues_for_line(HbaLine *hba, Datum *values, bool *nulls, bool hba_match_result,
+ 					   char *reason)
+ {
+ 	ListCell   *dbcell;
+ 	char		buffer[NI_MAXHOST];
+ 	int			index = 0;
+ 	JsonbParseState *parseState = NULL;
+ 	JsonbValue *result;
+ 
+ 	/* line_number */
+ 	values[index] = Int32GetDatum(hba->linenumber);
+ 
+ 	/* mode */
+ 	index++;
+ 	if (hba_match_result)
+ 		values[index] = CStringGetTextDatum("matched");
+ 	else
+ 		values[index] = CStringGetTextDatum("skipped");
+ 
+ 	/* 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;
+ 		default:
+ 			elog(ERROR, "unexpected connection type in parsed HBA entry");
+ 			break;
+ 	}
+ 
+ 	/* database */
+ 	index++;
+ 	result = pushJsonbValue(&parseState, WJB_BEGIN_ARRAY, NULL);
+ 
+ 	if (list_length(hba->databases) != 0)
+ 	{
+ 		HbaToken   *tok;
+ 
+ 		foreach(dbcell, hba->databases)
+ 		{
+ 			tok = lfirst(dbcell);
+ 			hba_add_jsonb_string_elem(&parseState, tok->string);
+ 		}
+ 	}
+ 
+ 	result = pushJsonbValue(&parseState, WJB_END_ARRAY, NULL);
+ 	values[index] = PointerGetDatum(JsonbValueToJsonb(result));
+ 
+ 	/* user */
+ 	index++;
+ 	result = pushJsonbValue(&parseState, WJB_BEGIN_ARRAY, NULL);
+ 
+ 	if (list_length(hba->roles) != 0)
+ 	{
+ 		HbaToken   *tok;
+ 
+ 		foreach(dbcell, hba->roles)
+ 		{
+ 			tok = lfirst(dbcell);
+ 			hba_add_jsonb_string_elem(&parseState, tok->string);
+ 		}
+ 	}
+ 
+ 	result = pushJsonbValue(&parseState, WJB_END_ARRAY, NULL);
+ 	values[index] = PointerGetDatum(JsonbValueToJsonb(result));
+ 
+ 	/* address */
+ 	index++;
+ 	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+ 	}
+ 	else
+ 		nulls[index] = true;
+ 
+ 	/* hostname */
+ 	index++;
+ 	if (hba->hostname)
+ 		values[index] = CStringGetTextDatum(hba->hostname);
+ 	else
+ 		nulls[index] = true;
+ 
+ 	/* method */
+ 	index++;
+ 	values[index] = getauthmethod(hba->auth_method);
+ 
+ 	/* options */
+ 	index++;
+ 	result = pushJsonbValue(&parseState, WJB_BEGIN_OBJECT, NULL);
+ 
+ 	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
+ 	{
+ 		if (hba->include_realm)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "include_realm");
+ 			hba_add_jsonb_bool_value(&parseState, true);
+ 		}
+ 
+ 		if (hba->krb_realm)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "krb_realm");
+ 			hba_add_jsonb_string_value(&parseState, hba->krb_realm);
+ 		}
+ 	}
+ 
+ 	if (hba->usermap)
+ 	{
+ 		hba_add_jsonb_string_key(&parseState, "map");
+ 		hba_add_jsonb_string_value(&parseState, hba->usermap);
+ 	}
+ 
+ 	if (hba->clientcert)
+ 	{
+ 		hba_add_jsonb_string_key(&parseState, "clientcert");
+ 		hba_add_jsonb_bool_value(&parseState, true);
+ 	}
+ 
+ 	if (hba->pamservice)
+ 	{
+ 		hba_add_jsonb_string_key(&parseState, "pamservice");
+ 		hba_add_jsonb_string_value(&parseState, hba->pamservice);
+ 	}
+ 
+ 	if (hba->auth_method == uaLDAP)
+ 	{
+ 		if (hba->ldapserver)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapserver");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapserver);
+ 		}
+ 
+ 		if (hba->ldapport)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapport");
+ 			hba_add_jsonb_int32_value(&parseState, hba->ldapport);
+ 		}
+ 
+ 		if (hba->ldaptls)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldaptls");
+ 			hba_add_jsonb_bool_value(&parseState, true);
+ 		}
+ 
+ 		if (hba->ldapprefix)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapprefix");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapprefix);
+ 		}
+ 
+ 		if (hba->ldapsuffix)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapsuffix");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapsuffix);
+ 		}
+ 
+ 		if (hba->ldapbasedn)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapbasedn");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapbasedn);
+ 		}
+ 
+ 		if (hba->ldapbinddn)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapbinddn");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapbinddn);
+ 		}
+ 
+ 		if (hba->ldapbindpasswd)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapbindpasswd");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapbindpasswd);
+ 		}
+ 
+ 		if (hba->ldapsearchattribute)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapsearchattribute");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapsearchattribute);
+ 		}
+ 
+ 		if (hba->ldapscope)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapscope");
+ 			hba_add_jsonb_int32_value(&parseState, hba->ldapscope);
+ 		}
+ 	}
+ 
+ 	if (hba->auth_method == uaRADIUS)
+ 	{
+ 		if (hba->radiusserver)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "radiusserver");
+ 			hba_add_jsonb_string_value(&parseState, hba->radiusserver);
+ 		}
+ 
+ 		if (hba->radiussecret)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "radiussecret");
+ 			hba_add_jsonb_string_value(&parseState, hba->radiussecret);
+ 		}
+ 
+ 		if (hba->radiusidentifier)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "radiusidentifier");
+ 			hba_add_jsonb_string_value(&parseState, hba->radiusidentifier);
+ 		}
+ 
+ 		if (hba->radiusport)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "radiusport");
+ 			hba_add_jsonb_int32_value(&parseState, hba->radiusport);
+ 		}
+ 	}
+ 
+ 	result = pushJsonbValue(&parseState, WJB_END_OBJECT, NULL);
+ 	values[index] = PointerGetDatum(JsonbValueToJsonb(result));
+ 
+ 	/* reason */
+ 	index++;
+ 	if (!hba_match_result)
+ 		values[index] = CStringGetTextDatum(reason);
+ 	else
+ 		nulls[index] = true;
+ }
+ 
+ static bool
+ pg_hba_match(HbaLine *hba, char *database, char *user, struct sockaddr_storage * addr,
+ 			 char *hostname, bool ssl_inuse, char *reason)
+ {
+ 	Oid			roleid;
+ 
+ 	if (ssl_inuse)
+ 	{
+ #ifdef USE_SSL
+ 		if (EnableSSL && hba->conntype != ctHostSSL)
+ 		{
+ 			strcpy(reason, "non SSL connection type");
+ 			return false;
+ 		}
+ 		else
+ 		{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 					 errmsg("hostssl requires SSL to be turned on"),
+ 					 errhint("Set ssl = on in postgresql.conf.")));
+ 			return false;
+ 		}
+ #else
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 				 errmsg("hostssl is not supported by this build"),
+ 			errhint("Compile with --with-openssl to use SSL connections.")));
+ 		return false;
+ #endif
+ 	}
+ 
+ 	/* Get the target role's OID.  Note we do not error out for bad role. */
+ 	roleid = get_role_oid(user, true);
+ 
+ 	if (!check_db(database, user, roleid, hba->databases))
+ 	{
+ 		strcpy(reason, "non matching database");
+ 		return false;
+ 	}
+ 
+ 	if (!check_role(user, roleid, hba->roles))
+ 	{
+ 		strcpy(reason, "non matching role");
+ 		return false;
+ 	}
+ 
+ 	/* Check IP address */
+ 	switch (hba->ip_cmp_method)
+ 	{
+ 		case ipCmpMask:
+ 			if (hostname && hba->hostname)
+ 			{
+ 				if (!hostname_match(hostname, hba->hostname))
+ 				{
+ 					strcpy(reason, "non matching hostname");
+ 					return false;
+ 				}
+ 			}
+ 			else
+ 			{
+ 				if (addr && !check_ip((SockAddr *) addr,
+ 									  (struct sockaddr *) & hba->addr,
+ 									  (struct sockaddr *) & hba->mask))
+ 				{
+ 					strcpy(reason, "non matching IP");
+ 					return false;
+ 				}
+ 			}
+ 			break;
+ 		case ipCmpAll:
+ 			break;
+ 		case ipCmpSameHost:
+ 		case ipCmpSameNet:
+ 			if (addr && !check_same_host_or_net((SockAddr *) addr,
+ 												hba->ip_cmp_method))
+ 			{
+ 				strcpy(reason, "non matching SameHost/SameNet");
+ 				return false;
+ 			}
+ 			break;
+ 		default:				/* Not reachable case */
+ 			break;
+ 	}
+ 
+ 	return true;
+ }
+ 
+ 
+ #define NUM_PG_HBA_CONF_ATTS   10
+ 
+ static void
+ pg_hba_lookup_internal(char *database, char *user, struct sockaddr_storage * addr,
+ 					   char *hostname, bool ssl_inuse, ReturnSetInfo *rsi)
+ {
+ 	Tuplestorestate *tuple_store;
+ 	TupleDesc	tupdesc;
+ 	ListCell   *line;
+ 	MemoryContext old_cxt;
+ 	MemoryContext pg_hba_tuple_context;
+ 
+ 	/*
+ 	 * Create the tupledesc and tuplestore in the per_query context as
+ 	 * required for SFRM_Materialize.
+ 	 */
+ 	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+ 
+ 	tupdesc = CreateTemplateTupleDesc(NUM_PG_HBA_CONF_ATTS, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "line_number",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "mode",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "type",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "database",
+ 					   JSONBOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "user_name",
+ 					   JSONBOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "address",
+ 					   INETOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "hostname",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "method",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "options",
+ 					   JSONBOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "reason",
+ 					   TEXTOID, -1, 0);
+ 	BlessTupleDesc(tupdesc);
+ 
+ 	tuple_store =
+ 		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+ 							  false, work_mem);
+ 
+ 	MemoryContextSwitchTo(old_cxt);
+ 
+ 	pg_hba_tuple_context = AllocSetContextCreate(CurrentMemoryContext,
+ 												 "pg_hba_lookup tuple cxt",
+ 												 ALLOCSET_DEFAULT_MINSIZE,
+ 												 ALLOCSET_DEFAULT_INITSIZE,
+ 												 ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	/*
+ 	 * Loop through the list and deparse each entry as it comes, storing it in
+ 	 * the tuplestore. Any temporary memory allocations here live only for the
+ 	 * function call lifetime.
+ 	 */
+ 	foreach(line, parsed_hba_lines)
+ 	{
+ 		HbaLine    *hba = (HbaLine *) lfirst(line);
+ 		Datum		values[NUM_PG_HBA_CONF_ATTS];
+ 		bool		nulls[NUM_PG_HBA_CONF_ATTS];
+ 		HeapTuple	tuple;
+ 		bool		hba_match_result;
+ 		char		reason[MAX_LEN_OF_NON_MATCH_HBA_ENTRY_REASON];
+ 
+ 		memset(values, 0, sizeof(values));
+ 		memset(nulls, 0, sizeof(nulls));
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		MemoryContextReset(pg_hba_tuple_context);
+ 		old_cxt = MemoryContextSwitchTo(pg_hba_tuple_context);
+ 
+ 		hba_match_result = pg_hba_match(hba, database, user, addr, hostname,
+ 										ssl_inuse, reason);
+ 
+ 		/* Get the next parsed hba line values */
+ 		hba_getvalues_for_line(hba, values, nulls, hba_match_result, reason);
+ 
+ 		MemoryContextSwitchTo(old_cxt);
+ 
+ 		/* build a tuple */
+ 		tuple = heap_form_tuple(tupdesc, values, nulls);
+ 		tuplestore_puttuple(tuple_store, tuple);
+ 
+ 		/* Stop the loop processing, if matching entry is found */
+ 		if (hba_match_result)
+ 			break;
+ 	}
+ 
+ 	MemoryContextDelete(pg_hba_tuple_context);
+ 
+ 	rsi->setDesc = tupdesc;
+ 	rsi->setResult = tuple_store;
+ 	return;
+ }
+ 
+ /*
+  * SQL-accessible SRF to return all the settings from the pg_hba.conf
+  * file.
+  */
+ Datum
+ pg_hba_lookup(PG_FUNCTION_ARGS)
+ {
+ 	char	   *user;
+ 	char	   *database;
+ 	char	   *address = NULL;
+ 	char	   *hostname = NULL;
+ 	bool		ssl_inuse = false;
+ 	struct sockaddr_storage addr;
+ 	struct sockaddr_storage *addr_ptr = NULL;
+ 
+ 	/*
+ 	 * 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.
+ 	 */
+ 	ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("must be superuser to view pg_hba.conf settings"))));
+ 
+ 	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+ 		(rsi->allowedModes & SFRM_Materialize) == 0)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("set-valued function called in context that "
+ 						"cannot accept a set")));
+ 
+ 	if (PG_ARGISNULL(0))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 				 (errmsg("database name is required to match pg_hba configuration entry"))));
+ 	else
+ 		database = TextDatumGetCString(PG_GETARG_DATUM(0));
+ 
+ 	if (PG_ARGISNULL(1))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 				 (errmsg("user name is required to match pg_hba configuration entry"))));
+ 	else
+ 		user = TextDatumGetCString(PG_GETARG_DATUM(1));
+ 
+ 	if (!PG_ARGISNULL(2))
+ 	{
+ 		struct addrinfo *gai_result;
+ 		struct addrinfo hints;
+ 		int			ret;
+ 
+ 		address = TextDatumGetCString(PG_GETARG_DATUM(2));
+ 
+ 		/* Get the IP address either way */
+ 		hints.ai_flags = AI_NUMERICHOST;
+ 		hints.ai_family = AF_UNSPEC;
+ 		hints.ai_socktype = 0;
+ 		hints.ai_protocol = 0;
+ 		hints.ai_addrlen = 0;
+ 		hints.ai_canonname = NULL;
+ 		hints.ai_addr = NULL;
+ 		hints.ai_next = NULL;
+ 
+ 		ret = pg_getaddrinfo_all(address, NULL, &hints, &gai_result);
+ 		if (ret == 0 && gai_result)
+ 		{
+ 			memcpy(&addr, gai_result->ai_addr, gai_result->ai_addrlen);
+ 			addr_ptr = &addr;
+ 		}
+ 		else if (ret == EAI_NONAME)
+ 			hostname = pstrdup(address);
+ 		else
+ 		{
+ 			ereport(ERROR,
+ 					(errmsg("invalid IP address \"%s\": %s",
+ 							address, gai_strerror(ret))));
+ 			if (gai_result)
+ 				pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ 			return false;
+ 		}
+ 
+ 		pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ 	}
+ 
+ 	ssl_inuse = DatumGetBool(PG_GETARG_DATUM(3));
+ 
+ 	if (load_hba_failure)
+ 		ereport(WARNING,
+ 			 (errmsg("There was some failure in reloading pg_hba.conf file. "
+ 		   "The pg_hba.conf settings data may contains stale information")));
+ 
+ 	rsi->returnMode = SFRM_Materialize;
+ 
+ 	pg_hba_lookup_internal(database, user, addr_ptr, hostname, ssl_inuse, rsi);
+ 
+ 	PG_RETURN_NULL();
+ }
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 4005,4010 **** PostgresMain(int argc, char *argv[],
--- 4005,4022 ----
  		{
  			got_SIGHUP = false;
  			ProcessConfigFile(PGC_SIGHUP);
+ 
+ 			/* 
+ 			 * Reload authentication config files too to refresh 
+ 			 * pg_hba_conf view data.
+ 			 */
+ 			load_hba_failure = false;
+ 			if (!load_hba())
+ 			{
+ 				ereport(DEBUG1,
+ 					(errmsg("Falure in reloading pg_hba.conf, pg_hba_conf view may show stale information")));
+ 				load_hba_failure = true;
+ 			}
  		}
  
  		/*
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3079,3084 **** DATA(insert OID = 2084 (  pg_show_all_settings	PGNSP PGUID 12 1 1000 0 0 f f f f
--- 3079,3086 ----
  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 = 3997 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f f t v u 4 0 2249 "25 25 25 16" "{25,25,25,16,23,25,25,3802,3802,869,25,25,3802,25}" "{i,i,i,i,o,o,o,o,o,o,o,o,o,o}" "{database,user_name,address,ssl_inuse,line_number,mode,type,database,user_name,address,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup _null_ _null_ _null_));
+ DESCR("view client authentication settings");
  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 = 1065 (  pg_prepared_xact PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{28,25,1184,26,26}" "{o,o,o,o,o}" "{transaction,gid,prepared,ownerid,dbid}" _null_ _null_ pg_prepared_xact _null_ _null_ _null_ ));
*** a/src/include/libpq/hba.h
--- b/src/include/libpq/hba.h
***************
*** 96,101 **** typedef struct IdentLine
--- 96,103 ----
  /* kluge to avoid including libpq/libpq-be.h here */
  typedef struct Port hbaPort;
  
+ extern bool load_hba_failure;
+ 
  extern bool load_hba(void);
  extern bool load_ident(void);
  extern void hba_getauthmethod(hbaPort *port);
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 1123,1128 **** extern Datum set_config_by_name(PG_FUNCTION_ARGS);
--- 1123,1131 ----
  extern Datum show_all_settings(PG_FUNCTION_ARGS);
  extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
  
+ /* hba.c */
+ extern Datum pg_hba_lookup(PG_FUNCTION_ARGS);
+ 
  /* rls.c */
  extern Datum row_security_active(PG_FUNCTION_ARGS);
  extern Datum row_security_active_name(PG_FUNCTION_ARGS);
#38Shulgin, Oleksandr
oleksandr.shulgin@zalando.de
In reply to: Haribabu Kommi (#37)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Wed, Dec 16, 2015 at 9:33 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

Function is changed to accept default values.

Apart from the above, added a local memory context to allocate the memory
required for forming tuple for each line. This context resets for every
hba line
to avoid consuming unnecessary memory for scenarios of huge pg_hba.conf
files.

In the revert_hba_context_release_in_backend patch, apart from reverting
the commit - 1e24cf64. In tokenize_file function, changed the new context
allocation from CurrentMemoryContext instead of TopMemoryContext.

Patch apply process:
1. revert_hba_context_release_in_backend_2.patch
2. pg_hba_lookup_poc_v7.patch

Hello,

1. Have you considered re-loading the HBA file upon call to this function
in a local context instead of keeping it in the backends memory? I do not
expect that the revert of 1e24cf645d24aab3ea39a9d259897fd0cae4e4b6 would be
accepted, as the commit message refers to potential security problems with
keeping this data in backend memory:

... This saves a
probably-usually-negligible amount of space per running backend. It
also
avoids leaving potentially-security-sensitive data lying around in
memory
in processes that don't need it. You'd have to be unusually paranoid to
think that that amounts to a live security bug, so I've not gone so far
as
to forcibly zero the memory; but there surely isn't a good reason to
keep
this data around.

2. I also wonder why JSONB arrays for database/user instead of TEXT[]?

3. What happens with special keywords for database column like
sameuser/samerole/samegroup and for special values in the user column?

4. Would it be possible to also include the raw unparsed line from the HBA
file? Just the line number is probably enough when you have access to the
host, but to show the results to someone else you might need to copy the
raw line manually. Not a big deal anyway.

5. Some tests demonstrating possible output would be really nice to have.

Cheers!
--
Alex

#39Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Shulgin, Oleksandr (#38)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Wed, Dec 23, 2015 at 8:54 PM, Shulgin, Oleksandr
<oleksandr.shulgin@zalando.de> wrote:

On Wed, Dec 16, 2015 at 9:33 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

Function is changed to accept default values.

Apart from the above, added a local memory context to allocate the memory
required for forming tuple for each line. This context resets for every
hba line
to avoid consuming unnecessary memory for scenarios of huge pg_hba.conf
files.

In the revert_hba_context_release_in_backend patch, apart from reverting
the commit - 1e24cf64. In tokenize_file function, changed the new context
allocation from CurrentMemoryContext instead of TopMemoryContext.

Patch apply process:
1. revert_hba_context_release_in_backend_2.patch
2. pg_hba_lookup_poc_v7.patch

Hello,

1. Have you considered re-loading the HBA file upon call to this function in
a local context instead of keeping it in the backends memory? I do not
expect that the revert of 1e24cf645d24aab3ea39a9d259897fd0cae4e4b6 would be
accepted, as the commit message refers to potential security problems with
keeping this data in backend memory:

... This saves a
probably-usually-negligible amount of space per running backend. It
also
avoids leaving potentially-security-sensitive data lying around in
memory
in processes that don't need it. You'd have to be unusually paranoid to
think that that amounts to a live security bug, so I've not gone so far
as
to forcibly zero the memory; but there surely isn't a good reason to
keep
this data around.

Yes, it is possible to load the file locally whenever the lookup
function is called.
Only thing i am considering is performance impact because of huge file load
whenever the function is called.

2. I also wonder why JSONB arrays for database/user instead of TEXT[]?

When I first tried this functionality as a view, it became very
difficult to deal with
keyword database and user names, so at that time, it was thought to use jsonb
instead of text[], thinking of easy to handle the keywords. But later decided to
drop the view approach itself. I can change it if others also feel the same.

3. What happens with special keywords for database column like
sameuser/samerole/samegroup and for special values in the user column?

There is no special handling for the keywords in this approach. Based on the
inputs to the function, it checks for the matched line in all hba lines.

For example, if a configuration line contains 'all' for database and user names,
then if user gives any database name and user name this line will be matched
and returned.

4. Would it be possible to also include the raw unparsed line from the HBA
file? Just the line number is probably enough when you have access to the
host, but to show the results to someone else you might need to copy the raw
line manually. Not a big deal anyway.

IMO as we are already showing all line information in columns
separately, it may not
be looks good.

5. Some tests demonstrating possible output would be really nice to have.

Do you mean regression tests? In case of install check case, the results are
based on the server configuration that is running. It may be difficult to write
tests to pass in all scenarios. Because of this reason i didn't add them.

Regards,
Hari Babu
Fujitsu Australia

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#40Michael Paquier
michael.paquier@gmail.com
In reply to: Haribabu Kommi (#39)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Wed, Dec 23, 2015 at 8:56 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

On Wed, Dec 23, 2015 at 8:54 PM, Shulgin, Oleksandr <oleksandr.shulgin@zalando.de> wrote:

5. Some tests demonstrating possible output would be really nice to have.

Do you mean regression tests? In case of install check case, the results are
based on the server configuration that is running. It may be difficult to write
tests to pass in all scenarios. Because of this reason i didn't add them.

We have an infrastructure in src/test/perl to set up servers with
specific configurations and write regression tests. You should just
use that,

(Moved this patch to next CF as discussion is still actively going on).
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#41Shulgin, Oleksandr
oleksandr.shulgin@zalando.de
In reply to: Haribabu Kommi (#39)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Wed, Dec 23, 2015 at 12:56 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Wed, Dec 23, 2015 at 8:54 PM, Shulgin, Oleksandr
<oleksandr.shulgin@zalando.de> wrote:

1. Have you considered re-loading the HBA file upon call to this

function in

a local context instead of keeping it in the backends memory? I do not
expect that the revert of 1e24cf645d24aab3ea39a9d259897fd0cae4e4b6 would

be

accepted, as the commit message refers to potential security problems

with

keeping this data in backend memory:

... This saves a
probably-usually-negligible amount of space per running backend. It
also
avoids leaving potentially-security-sensitive data lying around in
memory
in processes that don't need it. You'd have to be unusually

paranoid to

think that that amounts to a live security bug, so I've not gone so

far

as
to forcibly zero the memory; but there surely isn't a good reason to
keep
this data around.

Yes, it is possible to load the file locally whenever the lookup
function is called.
Only thing i am considering is performance impact because of huge file load
whenever the function is called.

Not sure why do you call it huge? Anyway since this is going to be used to
debug connection problems, I would expect the performance impact to be
insignificant.

On the other hand, re-loading the HBA file in the function will enable the
DBA to test if a proposed change to the HBA file fixes the client problem
w/o making the server to reload the config.

3. What happens with special keywords for database column like

sameuser/samerole/samegroup and for special values in the user column?

There is no special handling for the keywords in this approach. Based on
the
inputs to the function, it checks for the matched line in all hba lines.

For example, if a configuration line contains 'all' for database and user
names,
then if user gives any database name and user name this line will be
matched
and returned.

Now I wonder why are you trying to re-implement the checks found in hba.c's
check_hba() instead of extending this function to provide textual reason(s)
for skipping/rejection?

--
Alex

#42Tom Lane
tgl@sss.pgh.pa.us
In reply to: Shulgin, Oleksandr (#38)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

"Shulgin, Oleksandr" <oleksandr.shulgin@zalando.de> writes:

1. Have you considered re-loading the HBA file upon call to this function
in a local context instead of keeping it in the backends memory?

Aside from the security questions, please consider that this feature should
work similarly to the current implementation of the pg_file_settings view,
namely it tells you about what is *currently* in the on-disk files, not
necessarily what is the active setting in the postmaster's memory.
A backend could not be entirely sure about the postmaster's state anyway;
and even if it could be, one of the major applications for features like
this is testing manual changes to the files before you SIGHUP the
postmaster. So re-reading the files on each usage is a Good Thing, IMO,
even if it sounds inefficient.

2. I also wonder why JSONB arrays for database/user instead of TEXT[]?

Yes, that seems rather random to me too.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#43Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Tom Lane (#42)
1 attachment(s)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Thu, Dec 24, 2015 at 2:37 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

"Shulgin, Oleksandr" <oleksandr.shulgin@zalando.de> writes:

1. Have you considered re-loading the HBA file upon call to this function
in a local context instead of keeping it in the backends memory?

Aside from the security questions, please consider that this feature should
work similarly to the current implementation of the pg_file_settings view,
namely it tells you about what is *currently* in the on-disk files, not
necessarily what is the active setting in the postmaster's memory.
A backend could not be entirely sure about the postmaster's state anyway;
and even if it could be, one of the major applications for features like
this is testing manual changes to the files before you SIGHUP the
postmaster. So re-reading the files on each usage is a Good Thing, IMO,
even if it sounds inefficient.

2. I also wonder why JSONB arrays for database/user instead of TEXT[]?

Yes, that seems rather random to me too.

Here I attached updated patch with the following changes,
- Local loading of HBA file to show the authentication data
- Changed database and user types are text[]

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_hba_lookup_poc_v8.patchapplication/octet-stream; name=pg_hba_lookup_poc_v8.patchDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 16576,16581 **** SELECT collation for ('foo' COLLATE "de_DE");
--- 16576,16594 ----
         <entry><type>text</type></entry>
         <entry>set parameter and return new value</entry>
        </row>
+       <row>
+        <entry>
+         <indexterm>
+          <primary>pg_hba_lookup</primary>
+         </indexterm>
+         <literal><function>pg_hba_lookup(<parameter>database</> <type>text</>,
+                             <parameter>user_name</> <type>text</>
+                             [, <parameter>address</> <type>text</>]
+                             [, <parameter>ssl_inuse</> <type>text</>)</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>Returns all row entries of pg_hba.conf till matching entry is found</entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
***************
*** 16633,16638 **** SELECT set_config('log_statement_stats', 'off', false);
--- 16646,16669 ----
  </programlisting>
     </para>
  
+    <para>
+     <function>pg_hba_lookup</function> returns a set of records
+     containing the line number, mode, type, database, user_name,
+     address, hostname, method, options and reason. Typical usages
+     include:
+ <programlisting>
+ postgres=# select * from pg_hba_lookup('postgres', 'kommih','::1');
+  line_number |  mode   | type  | database | user_name |  address  | hostname | method | options |     reason      
+ -------------+---------+-------+----------+-----------+-----------+----------+--------+---------+-----------------
+           84 | skipped | local | {all}    | {all}     |           |          | trust  | {}      | non matching IP
+           86 | skipped | host  | {all}    | {all}     | 127.0.0.1 |          | trust  | {}      | non matching IP
+           88 | matched | host  | {all}    | {all}     | ::1       |          | trust  | {}      | 
+ (3 rows)
+ 
+ </programlisting>
+     Only super user can access this function to view the pg_hba entries.
+    </para>
+    
    </sect2>
  
    <sect2 id="functions-admin-signal">
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 948,950 **** RETURNS jsonb
--- 948,959 ----
  LANGUAGE INTERNAL
  STRICT IMMUTABLE
  AS 'jsonb_set';
+ 
+ CREATE OR REPLACE FUNCTION pg_hba_lookup(IN database text, IN user_name text,
+     IN address text default NULL, IN ssl_inuse boolean default false,
+ 	OUT line_number int, OUT mode text, OUT type text, OUT database _text,
+ 	OUT user_name _text, OUT address inet,	OUT hostname text, OUT method text,
+ 	OUT options jsonb, OUT reason text)
+ RETURNS SETOF RECORD
+ LANGUAGE INTERNAL
+ AS 'pg_hba_lookup';
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 25,39 ****
--- 25,46 ----
  #include <arpa/inet.h>
  #include <unistd.h>
  
+ #include "access/htup_details.h"
  #include "catalog/pg_collation.h"
+ #include "catalog/pg_type.h"
+ #include "catalog/objectaddress.h"
+ #include "funcapi.h"
  #include "libpq/ip.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/jsonb.h"
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  
***************
*** 52,57 ****
--- 59,67 ----
  #define MAX_TOKEN	256
  #define MAX_LINE	8192
  
+ #define NUM_PG_HBA_CONF_ATTS   10
+ #define MAX_LEN_OF_NON_MATCH_HBA_ENTRY_REASON 256
+ 
  /* callback data for check_network_callback */
  typedef struct check_network_data
  {
***************
*** 99,104 **** static List *tokenize_inc_file(List *tokens, const char *outer_filename,
--- 109,124 ----
  				  const char *inc_filename);
  static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
  				   int line_num);
+ static Datum getauthmethod(UserAuth auth_method);
+ static void hba_add_jsonb_string_key(JsonbParseState **pstate, char *string_key);
+ static void hba_add_jsonb_bool_value(JsonbParseState **pstate, bool bool_val);
+ static void hba_add_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val);
+ static void hba_add_jsonb_string_value(JsonbParseState **pstate, char *string_value);
+ static Jsonb *gethba_options(HbaLine *hba);
+ static void get_hbaline_values(char *database, char *user, struct sockaddr_storage * addr,
+ 				   char *hostname, bool ssl_inuse, ReturnSetInfo *rsi);
+ static bool pg_hba_lookup_match(HbaLine *hba, char *user, char *database, struct sockaddr_storage * addr,
+ 					char *hostname, bool ssl_inuse, char *reason);
  
  /*
   * isblank() exists in the ISO C99 spec, but it's not very portable yet,
***************
*** 1755,1760 **** load_hba(void)
--- 1775,1781 ----
  	MemoryContext linecxt;
  	MemoryContext oldcxt;
  	MemoryContext hbacxt;
+ 	MemoryContext parentcxt = (!IsUnderPostmaster) ? PostmasterContext : CurrentMemoryContext;
  
  	file = AllocateFile(HbaFileName, "r");
  	if (file == NULL)
***************
*** 1770,1777 **** load_hba(void)
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	Assert(PostmasterContext);
! 	hbacxt = AllocSetContextCreate(PostmasterContext,
  								   "hba parser context",
  								   ALLOCSET_DEFAULT_MINSIZE,
  								   ALLOCSET_DEFAULT_MINSIZE,
--- 1791,1798 ----
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	Assert(parentcxt);
! 	hbacxt = AllocSetContextCreate(parentcxt,
  								   "hba parser context",
  								   ALLOCSET_DEFAULT_MINSIZE,
  								   ALLOCSET_DEFAULT_MINSIZE,
***************
*** 1829,1835 **** load_hba(void)
  	}
  
  	/* Loaded new file successfully, replace the one we use */
! 	if (parsed_hba_context != NULL)
  		MemoryContextDelete(parsed_hba_context);
  	parsed_hba_context = hbacxt;
  	parsed_hba_lines = new_parsed_lines;
--- 1850,1856 ----
  	}
  
  	/* Loaded new file successfully, replace the one we use */
! 	if (!IsUnderPostmaster && parsed_hba_context != NULL)
  		MemoryContextDelete(parsed_hba_context);
  	parsed_hba_context = hbacxt;
  	parsed_hba_lines = new_parsed_lines;
***************
*** 2235,2237 **** hba_getauthmethod(hbaPort *port)
--- 2256,2885 ----
  {
  	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 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 uaLDAP:
+ 			result = CStringGetTextDatum("ldap");
+ 			break;
+ 		case uaCert:
+ 			result = CStringGetTextDatum("cert");
+ 			break;
+ 		case uaRADIUS:
+ 			result = CStringGetTextDatum("radius");
+ 			break;
+ 		case uaPeer:
+ 			result = CStringGetTextDatum("peer");
+ 			break;
+ 		default:
+ 			elog(ERROR, "unexpected authentication method in parsed HBA entry");
+ 			break;
+ 	}
+ 
+ 	return result;
+ }
+ 
+ static void
+ hba_add_jsonb_string_key(JsonbParseState **pstate, char *string_key)
+ {
+ 	JsonbValue	jb;
+ 
+ 	jb.type = jbvString;
+ 	jb.val.string.len = strlen(string_key);
+ 	jb.val.string.val = pstrdup(string_key);
+ 	pushJsonbValue(pstate, WJB_KEY, &jb);
+ }
+ 
+ static void
+ hba_add_jsonb_bool_value(JsonbParseState **pstate, bool bool_val)
+ {
+ 	JsonbValue	jb;
+ 
+ 	jb.type = jbvBool;
+ 	jb.val.boolean = bool_val;
+ 
+ 	pushJsonbValue(pstate, WJB_VALUE, &jb);
+ }
+ 
+ static void
+ hba_add_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val)
+ {
+ 	JsonbValue	jb;
+ 	char		outputstr[64];
+ 
+ 	sprintf(outputstr, "%d", int32_val);
+ 	jb.type = jbvNumeric;
+ 	jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1));
+ 
+ 	pushJsonbValue(pstate, WJB_VALUE, &jb);
+ }
+ 
+ static void
+ hba_add_jsonb_string_value(JsonbParseState **pstate, char *string_value)
+ {
+ 	JsonbValue	jb;
+ 
+ 	jb.type = jbvString;
+ 	jb.val.string.len = strlen(string_value);
+ 	jb.val.string.val = pstrdup(string_value);
+ 	pushJsonbValue(pstate, WJB_VALUE, &jb);
+ }
+ 
+ static Jsonb *
+ gethba_options(HbaLine *hba)
+ {
+ 	JsonbParseState *parseState = NULL;
+ 	JsonbValue *result;
+ 
+ 	result = pushJsonbValue(&parseState, WJB_BEGIN_OBJECT, NULL);
+ 
+ 	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
+ 	{
+ 		if (hba->include_realm)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "include_realm");
+ 			hba_add_jsonb_bool_value(&parseState, true);
+ 		}
+ 
+ 		if (hba->krb_realm)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "krb_realm");
+ 			hba_add_jsonb_string_value(&parseState, hba->krb_realm);
+ 		}
+ 	}
+ 
+ 	if (hba->usermap)
+ 	{
+ 		hba_add_jsonb_string_key(&parseState, "map");
+ 		hba_add_jsonb_string_value(&parseState, hba->usermap);
+ 	}
+ 
+ 	if (hba->clientcert)
+ 	{
+ 		hba_add_jsonb_string_key(&parseState, "clientcert");
+ 		hba_add_jsonb_bool_value(&parseState, true);
+ 	}
+ 
+ 	if (hba->pamservice)
+ 	{
+ 		hba_add_jsonb_string_key(&parseState, "pamservice");
+ 		hba_add_jsonb_string_value(&parseState, hba->pamservice);
+ 	}
+ 
+ 	if (hba->auth_method == uaLDAP)
+ 	{
+ 		if (hba->ldapserver)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapserver");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapserver);
+ 		}
+ 
+ 		if (hba->ldapport)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapport");
+ 			hba_add_jsonb_int32_value(&parseState, hba->ldapport);
+ 		}
+ 
+ 		if (hba->ldaptls)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldaptls");
+ 			hba_add_jsonb_bool_value(&parseState, true);
+ 		}
+ 
+ 		if (hba->ldapprefix)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapprefix");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapprefix);
+ 		}
+ 
+ 		if (hba->ldapsuffix)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapsuffix");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapsuffix);
+ 		}
+ 
+ 		if (hba->ldapbasedn)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapbasedn");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapbasedn);
+ 		}
+ 
+ 		if (hba->ldapbinddn)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapbinddn");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapbinddn);
+ 		}
+ 
+ 		if (hba->ldapbindpasswd)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapbindpasswd");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapbindpasswd);
+ 		}
+ 
+ 		if (hba->ldapsearchattribute)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapsearchattribute");
+ 			hba_add_jsonb_string_value(&parseState, hba->ldapsearchattribute);
+ 		}
+ 
+ 		if (hba->ldapscope)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "ldapscope");
+ 			hba_add_jsonb_int32_value(&parseState, hba->ldapscope);
+ 		}
+ 	}
+ 
+ 	if (hba->auth_method == uaRADIUS)
+ 	{
+ 		if (hba->radiusserver)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "radiusserver");
+ 			hba_add_jsonb_string_value(&parseState, hba->radiusserver);
+ 		}
+ 
+ 		if (hba->radiussecret)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "radiussecret");
+ 			hba_add_jsonb_string_value(&parseState, hba->radiussecret);
+ 		}
+ 
+ 		if (hba->radiusidentifier)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "radiusidentifier");
+ 			hba_add_jsonb_string_value(&parseState, hba->radiusidentifier);
+ 		}
+ 
+ 		if (hba->radiusport)
+ 		{
+ 			hba_add_jsonb_string_key(&parseState, "radiusport");
+ 			hba_add_jsonb_int32_value(&parseState, hba->radiusport);
+ 		}
+ 	}
+ 
+ 	result = pushJsonbValue(&parseState, WJB_END_OBJECT, NULL);
+ 	return JsonbValueToJsonb(result);
+ }
+ 
+ /*
+  * Fill in suitable values to build a tuple representing the
+  * HbaLine provided
+  */
+ static void
+ get_hbaline_values(char *database, char *user, struct sockaddr_storage * addr,
+ 				   char *hostname, bool ssl_inuse, ReturnSetInfo *rsi)
+ {
+ 	ListCell   *line;
+ 	Tuplestorestate *tuple_store;
+ 	TupleDesc	tupdesc;
+ 	MemoryContext old_cxt;
+ 	MemoryContext pg_hba_tuple_context;
+ 
+ 	/*
+ 	 * Create the tupledesc and tuplestore in the per_query context as
+ 	 * required for SFRM_Materialize.
+ 	 */
+ 	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+ 
+ 	tupdesc = CreateTemplateTupleDesc(NUM_PG_HBA_CONF_ATTS, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "line_number",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "mode",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "type",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "database",
+ 					   TEXTARRAYOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "user_name",
+ 					   TEXTARRAYOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "address",
+ 					   INETOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "hostname",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "method",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "options",
+ 					   JSONBOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "reason",
+ 					   TEXTOID, -1, 0);
+ 	BlessTupleDesc(tupdesc);
+ 
+ 	tuple_store =
+ 		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+ 							  false, work_mem);
+ 
+ 	MemoryContextSwitchTo(old_cxt);
+ 
+ 	pg_hba_tuple_context = AllocSetContextCreate(CurrentMemoryContext,
+ 												 "pg_hba_lookup tuple cxt",
+ 												 ALLOCSET_DEFAULT_MINSIZE,
+ 												 ALLOCSET_DEFAULT_INITSIZE,
+ 												 ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ 	/*
+ 	 * Loop through the list and deparse each entry as it comes, storing it in
+ 	 * the tuplestore. Any temporary memory allocations here live only for the
+ 	 * function call lifetime.
+ 	 */
+ 	foreach(line, parsed_hba_lines)
+ 	{
+ 		HbaLine    *hba = (HbaLine *) lfirst(line);
+ 		Datum		values[NUM_PG_HBA_CONF_ATTS];
+ 		bool		nulls[NUM_PG_HBA_CONF_ATTS];
+ 		ListCell   *dbcell;
+ 		char		buffer[NI_MAXHOST];
+ 		char		reason[MAX_LEN_OF_NON_MATCH_HBA_ENTRY_REASON];
+ 		HeapTuple	tuple;
+ 		bool		hba_match_result;
+ 		int			index;
+ 
+ 		index = 0;
+ 		memset(values, 0, sizeof(values));
+ 		memset(nulls, 0, sizeof(nulls));
+ 
+ 		CHECK_FOR_INTERRUPTS();
+ 
+ 		MemoryContextReset(pg_hba_tuple_context);
+ 		old_cxt = MemoryContextSwitchTo(pg_hba_tuple_context);
+ 
+ 		hba_match_result = pg_hba_lookup_match(hba, database, user, addr, hostname,
+ 											   ssl_inuse, reason);
+ 
+ 		/* line_number */
+ 		values[index] = Int32GetDatum(hba->linenumber);
+ 
+ 		/* mode */
+ 		index++;
+ 		if (hba_match_result)
+ 			values[index] = CStringGetTextDatum("matched");
+ 		else
+ 			values[index] = CStringGetTextDatum("skipped");
+ 
+ 		/* 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;
+ 			default:
+ 				elog(ERROR, "unexpected connection type in parsed HBA entry");
+ 				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);
+ 			}
+ 
+ 			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);
+ 			}
+ 
+ 			values[index] = PointerGetDatum(strlist_to_textarray(roles));
+ 		}
+ 		else
+ 			nulls[index] = true;
+ 
+ 		/* address */
+ 		index++;
+ 		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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+ 		}
+ 		else
+ 			nulls[index] = true;
+ 
+ 		/* hostname */
+ 		index++;
+ 		if (hba->hostname)
+ 			values[index] = CStringGetTextDatum(hba->hostname);
+ 		else
+ 			nulls[index] = true;
+ 
+ 		/* method */
+ 		index++;
+ 		values[index] = getauthmethod(hba->auth_method);
+ 
+ 		/* options */
+ 		index++;
+ 		values[index] = PointerGetDatum(gethba_options(hba));
+ 
+ 		/* reason */
+ 		index++;
+ 		if (!hba_match_result)
+ 			values[index] = CStringGetTextDatum(reason);
+ 		else
+ 			nulls[index] = true;
+ 
+ 		MemoryContextSwitchTo(old_cxt);
+ 
+ 		/* build a tuple */
+ 		tuple = heap_form_tuple(tupdesc, values, nulls);
+ 		tuplestore_puttuple(tuple_store, tuple);
+ 
+ 		/* Stop the loop processing, if matching entry is found */
+ 		if (hba_match_result)
+ 			break;
+ 	}
+ 
+ 	MemoryContextDelete(pg_hba_tuple_context);
+ 
+ 	rsi->setDesc = tupdesc;
+ 	rsi->setResult = tuple_store;
+ 	return;
+ }
+ 
+ static bool
+ pg_hba_lookup_match(HbaLine *hba, char *database, char *user, struct sockaddr_storage * addr,
+ 					char *hostname, bool ssl_inuse, char *reason)
+ {
+ 	Oid			roleid;
+ 
+ 	if (ssl_inuse)
+ 	{
+ #ifdef USE_SSL
+ 		if (EnableSSL && hba->conntype != ctHostSSL)
+ 		{
+ 			strcpy(reason, "non SSL connection type");
+ 			return false;
+ 		}
+ 		else
+ 		{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 					 errmsg("hostssl requires SSL to be turned on"),
+ 					 errhint("Set ssl = on in postgresql.conf.")));
+ 			return false;
+ 		}
+ #else
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 				 errmsg("hostssl is not supported by this build"),
+ 			errhint("Compile with --with-openssl to use SSL connections.")));
+ 		return false;
+ #endif
+ 	}
+ 
+ 	/* Get the target role's OID.  Note we do not error out for bad role. */
+ 	roleid = get_role_oid(user, true);
+ 
+ 	if (!check_db(database, user, roleid, hba->databases))
+ 	{
+ 		strcpy(reason, "non matching database");
+ 		return false;
+ 	}
+ 
+ 	if (!check_role(user, roleid, hba->roles))
+ 	{
+ 		strcpy(reason, "non matching role");
+ 		return false;
+ 	}
+ 
+ 	/* Check IP address */
+ 	switch (hba->ip_cmp_method)
+ 	{
+ 		case ipCmpMask:
+ 			if (hostname && hba->hostname)
+ 			{
+ 				if (!hostname_match(hostname, hba->hostname))
+ 				{
+ 					strcpy(reason, "non matching hostname");
+ 					return false;
+ 				}
+ 			}
+ 			else
+ 			{
+ 				if (addr && !check_ip((SockAddr *) addr,
+ 									  (struct sockaddr *) & hba->addr,
+ 									  (struct sockaddr *) & hba->mask))
+ 				{
+ 					strcpy(reason, "non matching IP");
+ 					return false;
+ 				}
+ 			}
+ 			break;
+ 		case ipCmpAll:
+ 			break;
+ 		case ipCmpSameHost:
+ 		case ipCmpSameNet:
+ 			if (addr && !check_same_host_or_net((SockAddr *) addr,
+ 												hba->ip_cmp_method))
+ 			{
+ 				strcpy(reason, "non matching SameHost/SameNet");
+ 				return false;
+ 			}
+ 			break;
+ 		default:				/* Not reachable case */
+ 			break;
+ 	}
+ 
+ 	return true;
+ }
+ 
+ /*
+  * SQL-accessible SRF to return all the settings from the pg_hba.conf
+  * file.
+  */
+ Datum
+ pg_hba_lookup(PG_FUNCTION_ARGS)
+ {
+ 	char	   *user;
+ 	char	   *database;
+ 	char	   *address = NULL;
+ 	char	   *hostname = NULL;
+ 	bool		ssl_inuse = false;
+ 	struct sockaddr_storage addr;
+ 	struct sockaddr_storage *addr_ptr = NULL;
+ 
+ 	/*
+ 	 * 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.
+ 	 */
+ 	ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("must be superuser to view pg_hba.conf settings"))));
+ 
+ 	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+ 		(rsi->allowedModes & SFRM_Materialize) == 0)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("set-valued function called in context that "
+ 						"cannot accept a set")));
+ 
+ 	if (PG_ARGISNULL(0))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 				 (errmsg("database name is required to match pg_hba configuration entry"))));
+ 	else
+ 		database = TextDatumGetCString(PG_GETARG_DATUM(0));
+ 
+ 	if (PG_ARGISNULL(1))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 				 (errmsg("user name is required to match pg_hba configuration entry"))));
+ 	else
+ 		user = TextDatumGetCString(PG_GETARG_DATUM(1));
+ 
+ 	if (!PG_ARGISNULL(2))
+ 	{
+ 		struct addrinfo *gai_result;
+ 		struct addrinfo hints;
+ 		int			ret;
+ 
+ 		address = TextDatumGetCString(PG_GETARG_DATUM(2));
+ 
+ 		/* Get the IP address either way */
+ 		hints.ai_flags = AI_NUMERICHOST;
+ 		hints.ai_family = AF_UNSPEC;
+ 		hints.ai_socktype = 0;
+ 		hints.ai_protocol = 0;
+ 		hints.ai_addrlen = 0;
+ 		hints.ai_canonname = NULL;
+ 		hints.ai_addr = NULL;
+ 		hints.ai_next = NULL;
+ 
+ 		ret = pg_getaddrinfo_all(address, NULL, &hints, &gai_result);
+ 		if (ret == 0 && gai_result)
+ 		{
+ 			memcpy(&addr, gai_result->ai_addr, gai_result->ai_addrlen);
+ 			addr_ptr = &addr;
+ 		}
+ 		else if (ret == EAI_NONAME)
+ 			hostname = pstrdup(address);
+ 		else
+ 		{
+ 			ereport(ERROR,
+ 					(errmsg("invalid IP address \"%s\": %s",
+ 							address, gai_strerror(ret))));
+ 			if (gai_result)
+ 				pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ 			return false;
+ 		}
+ 
+ 		pg_freeaddrinfo_all(hints.ai_family, gai_result);
+ 	}
+ 
+ 	ssl_inuse = DatumGetBool(PG_GETARG_DATUM(3));
+ 
+ 	rsi->returnMode = SFRM_Materialize;
+ 
+ 	if (!load_hba())
+ 		ereport(ERROR,
+ 			 (errmsg("There was some failure in loading pg_hba.conf file")));
+ 
+ 	/* Get the next parsed hba line values */
+ 	get_hbaline_values(database, user, addr_ptr, hostname, ssl_inuse, rsi);
+ 
+ 	PG_RETURN_NULL();
+ }
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3079,3084 **** DATA(insert OID = 2084 (  pg_show_all_settings	PGNSP PGUID 12 1 1000 0 0 f f f f
--- 3079,3086 ----
  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 = 3997 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f f t v u 4 0 2249 "25 25 25 16" "{25,25,25,16,23,25,25,1009,1009,869,25,25,3802,25}" "{i,i,i,i,o,o,o,o,o,o,o,o,o,o}" "{database,user_name,address,ssl_inuse,line_number,mode,type,database,user_name,address,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup _null_ _null_ _null_));
+ DESCR("view client authentication settings");
  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 = 1065 (  pg_prepared_xact PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{28,25,1184,26,26}" "{o,o,o,o,o}" "{transaction,gid,prepared,ownerid,dbid}" _null_ _null_ pg_prepared_xact _null_ _null_ _null_ ));
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 1123,1128 **** extern Datum set_config_by_name(PG_FUNCTION_ARGS);
--- 1123,1131 ----
  extern Datum show_all_settings(PG_FUNCTION_ARGS);
  extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
  
+ /* hba.c */
+ extern Datum pg_hba_lookup(PG_FUNCTION_ARGS);
+ 
  /* rls.c */
  extern Datum row_security_active(PG_FUNCTION_ARGS);
  extern Datum row_security_active_name(PG_FUNCTION_ARGS);
#44Shulgin, Oleksandr
oleksandr.shulgin@zalando.de
In reply to: Haribabu Kommi (#43)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Thu, Dec 24, 2015 at 5:16 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Thu, Dec 24, 2015 at 2:37 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

"Shulgin, Oleksandr" <oleksandr.shulgin@zalando.de> writes:

1. Have you considered re-loading the HBA file upon call to this

function

in a local context instead of keeping it in the backends memory?

Aside from the security questions, please consider that this feature

should

work similarly to the current implementation of the pg_file_settings

view,

namely it tells you about what is *currently* in the on-disk files, not
necessarily what is the active setting in the postmaster's memory.
A backend could not be entirely sure about the postmaster's state anyway;
and even if it could be, one of the major applications for features like
this is testing manual changes to the files before you SIGHUP the
postmaster. So re-reading the files on each usage is a Good Thing, IMO,
even if it sounds inefficient.

2. I also wonder why JSONB arrays for database/user instead of TEXT[]?

Yes, that seems rather random to me too.

Here I attached updated patch with the following changes,
- Local loading of HBA file to show the authentication data
- Changed database and user types are text[]

Still this requires a revert of the memory context handling commit for
load_hba() and load_ident(). I think you can get around the problem by
changing these functions to work with CurrentMemoryContext and set it
explicitly to the newly allocated PostmasterContext in
PerformAuthentication(). In your function you could then create a
temporary context to be discarded before leaving the function.

I still think you should not try to re-implement check_hba(), but extend
this function with means to report line skip reasons as per your
requirements. Having an optional callback function might be a good fit (a
possible use case is logging the reasons line by line).

Thank you.
--
Alex

#45Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Shulgin, Oleksandr (#44)
1 attachment(s)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Mon, Dec 28, 2015 at 9:09 PM, Shulgin, Oleksandr
<oleksandr.shulgin@zalando.de> wrote:

On Thu, Dec 24, 2015 at 5:16 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Thu, Dec 24, 2015 at 2:37 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

"Shulgin, Oleksandr" <oleksandr.shulgin@zalando.de> writes:

1. Have you considered re-loading the HBA file upon call to this
function
in a local context instead of keeping it in the backends memory?

Aside from the security questions, please consider that this feature
should
work similarly to the current implementation of the pg_file_settings
view,
namely it tells you about what is *currently* in the on-disk files, not
necessarily what is the active setting in the postmaster's memory.
A backend could not be entirely sure about the postmaster's state
anyway;
and even if it could be, one of the major applications for features like
this is testing manual changes to the files before you SIGHUP the
postmaster. So re-reading the files on each usage is a Good Thing, IMO,
even if it sounds inefficient.

2. I also wonder why JSONB arrays for database/user instead of TEXT[]?

Yes, that seems rather random to me too.

Here I attached updated patch with the following changes,
- Local loading of HBA file to show the authentication data
- Changed database and user types are text[]

Still this requires a revert of the memory context handling commit for
load_hba() and load_ident(). I think you can get around the problem by
changing these functions to work with CurrentMemoryContext and set it
explicitly to the newly allocated PostmasterContext in
PerformAuthentication(). In your function you could then create a temporary
context to be discarded before leaving the function.

Thanks for the review. I didn't understand your point clearly.

In the attached patch, load_hba uses PostmasterContext if it is present,
otherwise CurretMemoryContext. PostmasterContext is present only
in the backend start phase.

I still think you should not try to re-implement check_hba(), but extend
this function with means to report line skip reasons as per your
requirements. Having an optional callback function might be a good fit (a
possible use case is logging the reasons line by line).

check_hba function is enhanced to fill the hba line details with
reason for mismatch.
In check_hba function whenever a mismatch is found, the fill_hbaline function is
called to frame the tuple and inserted into tuple store.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_hba_lookup_poc_v9.patchapplication/octet-stream; name=pg_hba_lookup_poc_v9.patchDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 16576,16581 **** SELECT collation for ('foo' COLLATE "de_DE");
--- 16576,16594 ----
         <entry><type>text</type></entry>
         <entry>set parameter and return new value</entry>
        </row>
+       <row>
+        <entry>
+         <indexterm>
+          <primary>pg_hba_lookup</primary>
+         </indexterm>
+         <literal><function>pg_hba_lookup(<parameter>database</> <type>text</>,
+                             <parameter>user_name</> <type>text</>
+                             [, <parameter>address</> <type>text</>]
+                             [, <parameter>ssl_inuse</> <type>text</>)</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>Returns all row entries of pg_hba.conf till matching entry is found</entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
***************
*** 16633,16638 **** SELECT set_config('log_statement_stats', 'off', false);
--- 16646,16669 ----
  </programlisting>
     </para>
  
+    <para>
+     <function>pg_hba_lookup</function> returns a set of records
+     containing the line number, mode, type, database, user_name,
+     address, hostname, method, options and reason. Typical usages
+     include:
+ <programlisting>
+ postgres=# select * from pg_hba_lookup('postgres', 'kommih','::1');
+  line_number |  mode   | type  | database | user_name |  address  | hostname | method | options |     reason      
+ -------------+---------+-------+----------+-----------+-----------+----------+--------+---------+-----------------
+           84 | skipped | local | {all}    | {all}     |           |          | trust  | {}      | non matching IP
+           86 | skipped | host  | {all}    | {all}     | 127.0.0.1 |          | trust  | {}      | non matching IP
+           88 | matched | host  | {all}    | {all}     | ::1       |          | trust  | {}      | 
+ (3 rows)
+ 
+ </programlisting>
+     Only super user can access this function to view the pg_hba entries.
+    </para>
+    
    </sect2>
  
    <sect2 id="functions-admin-signal">
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 948,950 **** RETURNS jsonb
--- 948,959 ----
  LANGUAGE INTERNAL
  STRICT IMMUTABLE
  AS 'jsonb_set';
+ 
+ CREATE OR REPLACE FUNCTION pg_hba_lookup(IN database text, IN user_name text,
+     IN address text default NULL, IN ssl_inuse boolean default false,
+ 	OUT line_number int, OUT mode text, OUT type text, OUT database _text,
+ 	OUT user_name _text, OUT address inet,	OUT hostname text, OUT method text,
+ 	OUT options jsonb, OUT reason text)
+ RETURNS SETOF RECORD
+ LANGUAGE INTERNAL
+ AS 'pg_hba_lookup';
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 25,39 ****
--- 25,46 ----
  #include <arpa/inet.h>
  #include <unistd.h>
  
+ #include "access/htup_details.h"
  #include "catalog/pg_collation.h"
+ #include "catalog/pg_type.h"
+ #include "catalog/objectaddress.h"
+ #include "funcapi.h"
  #include "libpq/ip.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/jsonb.h"
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  
***************
*** 52,57 ****
--- 59,67 ----
  #define MAX_TOKEN	256
  #define MAX_LINE	8192
  
+ #define NUM_PG_HBA_CONF_ATTS   10
+ #define MAX_LEN_OF_NON_MATCH_HBA_ENTRY_REASON 256
+ 
  /* callback data for check_network_callback */
  typedef struct check_network_data
  {
***************
*** 99,104 **** static List *tokenize_inc_file(List *tokens, const char *outer_filename,
--- 109,122 ----
  				  const char *inc_filename);
  static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
  				   int line_num);
+ static Datum getauthmethod(UserAuth auth_method);
+ static void hba_add_jsonb_string_key(JsonbParseState **pstate, char *string_key);
+ static void hba_add_jsonb_bool_value(JsonbParseState **pstate, bool bool_val);
+ static void hba_add_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val);
+ static void hba_add_jsonb_string_value(JsonbParseState **pstate, char *string_value);
+ static Jsonb *gethba_options(HbaLine *hba);
+ static void fill_hbaline(HbaLine *hba, TupleDesc tupdesc, Tuplestorestate *tuple_store,
+ 			 char *reason);
  
  /*
   * isblank() exists in the ISO C99 spec, but it's not very portable yet,
***************
*** 1640,1646 **** parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
   *	request.
   */
  static void
! check_hba(hbaPort *port)
  {
  	Oid			roleid;
  	ListCell   *line;
--- 1658,1664 ----
   *	request.
   */
  static void
! check_hba(hbaPort *port, TupleDesc tupdesc, Tuplestorestate *tuple_store)
  {
  	Oid			roleid;
  	ListCell   *line;
***************
*** 1657,1681 **** check_hba(hbaPort *port)
--- 1675,1711 ----
  		if (hba->conntype == ctLocal)
  		{
  			if (!IS_AF_UNIX(port->raddr.addr.ss_family))
+ 			{
+ 				fill_hbaline(hba, tupdesc, tuple_store, "non matching connection type");
  				continue;
+ 			}
  		}
  		else
  		{
  			if (IS_AF_UNIX(port->raddr.addr.ss_family))
+ 			{
+ 				fill_hbaline(hba, tupdesc, tuple_store, "non matching connection type");
  				continue;
+ 			}
  
  			/* Check SSL state */
  			if (port->ssl_in_use)
  			{
  				/* Connection is SSL, match both "host" and "hostssl" */
  				if (hba->conntype == ctHostNoSSL)
+ 				{
+ 					fill_hbaline(hba, tupdesc, tuple_store, "non matching connection type");
  					continue;
+ 				}
  			}
  			else
  			{
  				/* Connection is not SSL, match both "host" and "hostnossl" */
  				if (hba->conntype == ctHostSSL)
+ 				{
+ 					fill_hbaline(hba, tupdesc, tuple_store, "non matching connection type");
  					continue;
+ 				}
  			}
  
  			/* Check IP address */
***************
*** 1686,1699 **** check_hba(hbaPort *port)
--- 1716,1735 ----
  					{
  						if (!check_hostname(port,
  											hba->hostname))
+ 						{
+ 							fill_hbaline(hba, tupdesc, tuple_store, "non matching hostname");
  							continue;
+ 						}
  					}
  					else
  					{
  						if (!check_ip(&port->raddr,
  									  (struct sockaddr *) & hba->addr,
  									  (struct sockaddr *) & hba->mask))
+ 						{
+ 							fill_hbaline(hba, tupdesc, tuple_store, "non matching IP");
  							continue;
+ 						}
  					}
  					break;
  				case ipCmpAll:
***************
*** 1702,1708 **** check_hba(hbaPort *port)
--- 1738,1747 ----
  				case ipCmpSameNet:
  					if (!check_same_host_or_net(&port->raddr,
  												hba->ip_cmp_method))
+ 					{
+ 						fill_hbaline(hba, tupdesc, tuple_store, "non matching samehost/samenet");
  						continue;
+ 					}
  					break;
  				default:
  					/* shouldn't get here, but deem it no-match if so */
***************
*** 1713,1722 **** check_hba(hbaPort *port)
--- 1752,1769 ----
  		/* Check database and role */
  		if (!check_db(port->database_name, port->user_name, roleid,
  					  hba->databases))
+ 		{
+ 			fill_hbaline(hba, tupdesc, tuple_store, "non matching database name");
  			continue;
+ 		}
  
  		if (!check_role(port->user_name, roleid, hba->roles))
+ 		{
+ 			fill_hbaline(hba, tupdesc, tuple_store, "non matching user name");
  			continue;
+ 		}
+ 
+ 		fill_hbaline(hba, tupdesc, tuple_store, NULL);
  
  		/* Found a record that matched! */
  		port->hba = hba;
***************
*** 1755,1760 **** load_hba(void)
--- 1802,1808 ----
  	MemoryContext linecxt;
  	MemoryContext oldcxt;
  	MemoryContext hbacxt;
+ 	MemoryContext parentcxt = PostmasterContext ? PostmasterContext : CurrentMemoryContext;
  
  	file = AllocateFile(HbaFileName, "r");
  	if (file == NULL)
***************
*** 1770,1777 **** load_hba(void)
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	Assert(PostmasterContext);
! 	hbacxt = AllocSetContextCreate(PostmasterContext,
  								   "hba parser context",
  								   ALLOCSET_DEFAULT_MINSIZE,
  								   ALLOCSET_DEFAULT_MINSIZE,
--- 1818,1825 ----
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	Assert(parentcxt);
! 	hbacxt = AllocSetContextCreate(parentcxt,
  								   "hba parser context",
  								   ALLOCSET_DEFAULT_MINSIZE,
  								   ALLOCSET_DEFAULT_MINSIZE,
***************
*** 1828,1836 **** load_hba(void)
  		return false;
  	}
  
- 	/* Loaded new file successfully, replace the one we use */
- 	if (parsed_hba_context != NULL)
- 		MemoryContextDelete(parsed_hba_context);
  	parsed_hba_context = hbacxt;
  	parsed_hba_lines = new_parsed_lines;
  
--- 1876,1881 ----
***************
*** 2233,2237 **** load_ident(void)
  void
  hba_getauthmethod(hbaPort *port)
  {
! 	check_hba(port);
  }
--- 2278,2806 ----
  void
  hba_getauthmethod(hbaPort *port)
  {
! 	check_hba(port, NULL, NULL);
! }
! 
! /*
!  * 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 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 uaLDAP:
! 			result = CStringGetTextDatum("ldap");
! 			break;
! 		case uaCert:
! 			result = CStringGetTextDatum("cert");
! 			break;
! 		case uaRADIUS:
! 			result = CStringGetTextDatum("radius");
! 			break;
! 		case uaPeer:
! 			result = CStringGetTextDatum("peer");
! 			break;
! 		default:
! 			elog(ERROR, "unexpected authentication method in parsed HBA entry");
! 			break;
! 	}
! 
! 	return result;
! }
! 
! static void
! hba_add_jsonb_string_key(JsonbParseState **pstate, char *string_key)
! {
! 	JsonbValue	jb;
! 
! 	jb.type = jbvString;
! 	jb.val.string.len = strlen(string_key);
! 	jb.val.string.val = pstrdup(string_key);
! 	pushJsonbValue(pstate, WJB_KEY, &jb);
! }
! 
! static void
! hba_add_jsonb_bool_value(JsonbParseState **pstate, bool bool_val)
! {
! 	JsonbValue	jb;
! 
! 	jb.type = jbvBool;
! 	jb.val.boolean = bool_val;
! 
! 	pushJsonbValue(pstate, WJB_VALUE, &jb);
! }
! 
! static void
! hba_add_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val)
! {
! 	JsonbValue	jb;
! 	char		outputstr[64];
! 
! 	sprintf(outputstr, "%d", int32_val);
! 	jb.type = jbvNumeric;
! 	jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1));
! 
! 	pushJsonbValue(pstate, WJB_VALUE, &jb);
! }
! 
! static void
! hba_add_jsonb_string_value(JsonbParseState **pstate, char *string_value)
! {
! 	JsonbValue	jb;
! 
! 	jb.type = jbvString;
! 	jb.val.string.len = strlen(string_value);
! 	jb.val.string.val = pstrdup(string_value);
! 	pushJsonbValue(pstate, WJB_VALUE, &jb);
! }
! 
! static Jsonb *
! gethba_options(HbaLine *hba)
! {
! 	JsonbParseState *parseState = NULL;
! 	JsonbValue *result;
! 
! 	result = pushJsonbValue(&parseState, WJB_BEGIN_OBJECT, NULL);
! 
! 	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
! 	{
! 		if (hba->include_realm)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "include_realm");
! 			hba_add_jsonb_bool_value(&parseState, true);
! 		}
! 
! 		if (hba->krb_realm)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "krb_realm");
! 			hba_add_jsonb_string_value(&parseState, hba->krb_realm);
! 		}
! 	}
! 
! 	if (hba->usermap)
! 	{
! 		hba_add_jsonb_string_key(&parseState, "map");
! 		hba_add_jsonb_string_value(&parseState, hba->usermap);
! 	}
! 
! 	if (hba->clientcert)
! 	{
! 		hba_add_jsonb_string_key(&parseState, "clientcert");
! 		hba_add_jsonb_bool_value(&parseState, true);
! 	}
! 
! 	if (hba->pamservice)
! 	{
! 		hba_add_jsonb_string_key(&parseState, "pamservice");
! 		hba_add_jsonb_string_value(&parseState, hba->pamservice);
! 	}
! 
! 	if (hba->auth_method == uaLDAP)
! 	{
! 		if (hba->ldapserver)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapserver");
! 			hba_add_jsonb_string_value(&parseState, hba->ldapserver);
! 		}
! 
! 		if (hba->ldapport)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapport");
! 			hba_add_jsonb_int32_value(&parseState, hba->ldapport);
! 		}
! 
! 		if (hba->ldaptls)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldaptls");
! 			hba_add_jsonb_bool_value(&parseState, true);
! 		}
! 
! 		if (hba->ldapprefix)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapprefix");
! 			hba_add_jsonb_string_value(&parseState, hba->ldapprefix);
! 		}
! 
! 		if (hba->ldapsuffix)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapsuffix");
! 			hba_add_jsonb_string_value(&parseState, hba->ldapsuffix);
! 		}
! 
! 		if (hba->ldapbasedn)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapbasedn");
! 			hba_add_jsonb_string_value(&parseState, hba->ldapbasedn);
! 		}
! 
! 		if (hba->ldapbinddn)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapbinddn");
! 			hba_add_jsonb_string_value(&parseState, hba->ldapbinddn);
! 		}
! 
! 		if (hba->ldapbindpasswd)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapbindpasswd");
! 			hba_add_jsonb_string_value(&parseState, hba->ldapbindpasswd);
! 		}
! 
! 		if (hba->ldapsearchattribute)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapsearchattribute");
! 			hba_add_jsonb_string_value(&parseState, hba->ldapsearchattribute);
! 		}
! 
! 		if (hba->ldapscope)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapscope");
! 			hba_add_jsonb_int32_value(&parseState, hba->ldapscope);
! 		}
! 	}
! 
! 	if (hba->auth_method == uaRADIUS)
! 	{
! 		if (hba->radiusserver)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "radiusserver");
! 			hba_add_jsonb_string_value(&parseState, hba->radiusserver);
! 		}
! 
! 		if (hba->radiussecret)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "radiussecret");
! 			hba_add_jsonb_string_value(&parseState, hba->radiussecret);
! 		}
! 
! 		if (hba->radiusidentifier)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "radiusidentifier");
! 			hba_add_jsonb_string_value(&parseState, hba->radiusidentifier);
! 		}
! 
! 		if (hba->radiusport)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "radiusport");
! 			hba_add_jsonb_int32_value(&parseState, hba->radiusport);
! 		}
! 	}
! 
! 	result = pushJsonbValue(&parseState, WJB_END_OBJECT, NULL);
! 	return JsonbValueToJsonb(result);
! }
! 
! static void
! fill_hbaline(HbaLine *hba, TupleDesc tupdesc, Tuplestorestate *tuple_store,
! 			 char *reason)
! {
! 	Datum		values[NUM_PG_HBA_CONF_ATTS];
! 	bool		nulls[NUM_PG_HBA_CONF_ATTS];
! 	ListCell   *dbcell;
! 	char		buffer[NI_MAXHOST];
! 	HeapTuple	tuple;
! 	int			index;
! 	MemoryContext old_cxt;
! 	MemoryContext pg_hba_tuple_context;
! 
! 	if (tupdesc == NULL || tuple_store == NULL)
! 		return;
! 
! 	index = 0;
! 	memset(values, 0, sizeof(values));
! 	memset(nulls, 0, sizeof(nulls));
! 
! 	pg_hba_tuple_context = AllocSetContextCreate(CurrentMemoryContext,
! 												 "pg_hba_lookup tuple cxt",
! 												 ALLOCSET_DEFAULT_MINSIZE,
! 												 ALLOCSET_DEFAULT_INITSIZE,
! 												 ALLOCSET_DEFAULT_MAXSIZE);
! 
! 	old_cxt = MemoryContextSwitchTo(pg_hba_tuple_context);
! 
! 	/* line_number */
! 	values[index] = Int32GetDatum(hba->linenumber);
! 
! 	/* mode */
! 	index++;
! 	if (reason == NULL)
! 		values[index] = CStringGetTextDatum("matched");
! 	else
! 		values[index] = CStringGetTextDatum("skipped");
! 
! 	/* 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;
! 		default:
! 			elog(ERROR, "unexpected connection type in parsed HBA entry");
! 			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);
! 		}
! 
! 		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);
! 		}
! 
! 		values[index] = PointerGetDatum(strlist_to_textarray(roles));
! 	}
! 	else
! 		nulls[index] = true;
! 
! 	/* address */
! 	index++;
! 	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
! 	}
! 	else
! 		nulls[index] = true;
! 
! 	/* hostname */
! 	index++;
! 	if (hba->hostname)
! 		values[index] = CStringGetTextDatum(hba->hostname);
! 	else
! 		nulls[index] = true;
! 
! 	/* method */
! 	index++;
! 	values[index] = getauthmethod(hba->auth_method);
! 
! 	/* options */
! 	index++;
! 	values[index] = PointerGetDatum(gethba_options(hba));
! 
! 	/* reason */
! 	index++;
! 	if (reason)
! 		values[index] = CStringGetTextDatum(reason);
! 	else
! 		nulls[index] = true;
! 
! 	tuple = heap_form_tuple(tupdesc, values, nulls);
! 	tuplestore_puttuple(tuple_store, tuple);
! 
! 	MemoryContextSwitchTo(old_cxt);
! 	MemoryContextDelete(pg_hba_tuple_context);
! 	return;
! }
! 
! /*
!  * SQL-accessible SRF to return all the settings from the pg_hba.conf
!  * file.
!  */
! Datum
! pg_hba_lookup(PG_FUNCTION_ARGS)
! {
! 	hbaPort    *port;
! 	Tuplestorestate *tuple_store;
! 	TupleDesc	tupdesc;
! 	MemoryContext old_cxt;
! 
! 	/*
! 	 * 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.
! 	 */
! 	ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
! 
! 	if (!superuser())
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser to view pg_hba.conf settings"))));
! 
! 	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
! 		(rsi->allowedModes & SFRM_Materialize) == 0)
! 		ereport(ERROR,
! 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("set-valued function called in context that "
! 						"cannot accept a set")));
! 
! 	port = palloc0(sizeof(hbaPort));
! 
! 	if (PG_ARGISNULL(0))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
! 				 (errmsg("database name is required to match pg_hba configuration entry"))));
! 	else
! 		port->database_name = TextDatumGetCString(PG_GETARG_DATUM(0));
! 
! 	if (PG_ARGISNULL(1))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
! 				 (errmsg("user name is required to match pg_hba configuration entry"))));
! 	else
! 		port->user_name = TextDatumGetCString(PG_GETARG_DATUM(1));
! 
! 
! 	if (!PG_ARGISNULL(2))
! 	{
! 		char	   *address = NULL;
! 		struct addrinfo *gai_result = NULL;
! 		struct addrinfo hints;
! 		int			ret;
! 
! 		address = TextDatumGetCString(PG_GETARG_DATUM(2));
! 
! 		/* Get the IP address either way */
! 		hints.ai_flags = AI_NUMERICHOST;
! 		hints.ai_family = AF_UNSPEC;
! 		hints.ai_socktype = 0;
! 		hints.ai_protocol = 0;
! 		hints.ai_addrlen = 0;
! 		hints.ai_canonname = NULL;
! 		hints.ai_addr = NULL;
! 		hints.ai_next = NULL;
! 
! 		ret = getaddrinfo(address, NULL, &hints, &gai_result);
! 		if (ret == 0 && gai_result)
! 			memcpy(&port->raddr.addr, gai_result->ai_addr, gai_result->ai_addrlen);
! 		else if (ret == EAI_NONAME)
! 		{
! 			struct addrinfo *gai_result2 = NULL;
! 
! 			port->remote_hostname = pstrdup(address);
! 
! 			ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result2);
! 			if (ret == 0 && gai_result2)
! 				memcpy(&port->raddr.addr, gai_result2->ai_addr, gai_result2->ai_addrlen);
! 			else
! 				ereport(ERROR,
! 				(errmsg("getaddrinfo failed to look into hostname \"%s\": %s",
! 						port->remote_hostname, gai_strerror(ret))));
! 
! 			if (gai_result2)
! 				freeaddrinfo(gai_result2);
! 		}
! 		else
! 			ereport(ERROR,
! 					(errmsg("invalid IP address \"%s\": %s",
! 							address, gai_strerror(ret))));
! 		if (gai_result)
! 			freeaddrinfo(gai_result);
! 	}
! 	else
! 		port->raddr.addr.ss_family = AF_UNIX;
! 
! 	port->ssl_in_use = DatumGetBool(PG_GETARG_DATUM(3));
! 
! 	rsi->returnMode = SFRM_Materialize;
! 
! 	if (!load_hba())
! 		ereport(ERROR,
! 			 (errmsg("There was some failure in loading pg_hba.conf file")));
! 
! 	/*
! 	 * Create the tupledesc and tuplestore in the per_query context as
! 	 * required for SFRM_Materialize.
! 	 */
! 	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
! 
! 	tupdesc = CreateTemplateTupleDesc(NUM_PG_HBA_CONF_ATTS, false);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "line_number",
! 					   INT4OID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "mode",
! 					   TEXTOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "type",
! 					   TEXTOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "database",
! 					   TEXTARRAYOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "user_name",
! 					   TEXTARRAYOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "address",
! 					   INETOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "hostname",
! 					   TEXTOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "method",
! 					   TEXTOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "options",
! 					   JSONBOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "reason",
! 					   TEXTOID, -1, 0);
! 	BlessTupleDesc(tupdesc);
! 
! 	tuple_store =
! 		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
! 							  false, work_mem);
! 
! 	MemoryContextSwitchTo(old_cxt);
! 
! 	check_hba(port, tupdesc, tuple_store);
! 
! 	rsi->setDesc = tupdesc;
! 	rsi->setResult = tuple_store;
! 
! 	MemoryContextDelete(parsed_hba_context);
! 	parsed_hba_context = NULL;
! 
! 	PG_RETURN_NULL();
  }
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3079,3084 **** DATA(insert OID = 2084 (  pg_show_all_settings	PGNSP PGUID 12 1 1000 0 0 f f f f
--- 3079,3086 ----
  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 = 3997 (pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f f t v u 4 0 2249 "25 25 25 16" "{25,25,25,16,23,25,25,1009,1009,869,25,25,3802,25}" "{i,i,i,i,o,o,o,o,o,o,o,o,o,o}" "{database,user_name,address,ssl_inuse,line_number,mode,type,database,user_name,address,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup _null_ _null_ _null_));
+ DESCR("view client authentication settings");
  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 = 1065 (  pg_prepared_xact PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{28,25,1184,26,26}" "{o,o,o,o,o}" "{transaction,gid,prepared,ownerid,dbid}" _null_ _null_ pg_prepared_xact _null_ _null_ _null_ ));
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 1123,1128 **** extern Datum set_config_by_name(PG_FUNCTION_ARGS);
--- 1123,1131 ----
  extern Datum show_all_settings(PG_FUNCTION_ARGS);
  extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
  
+ /* hba.c */
+ extern Datum pg_hba_lookup(PG_FUNCTION_ARGS);
+ 
  /* rls.c */
  extern Datum row_security_active(PG_FUNCTION_ARGS);
  extern Datum row_security_active_name(PG_FUNCTION_ARGS);
#46Shulgin, Oleksandr
oleksandr.shulgin@zalando.de
In reply to: Haribabu Kommi (#45)
1 attachment(s)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Tue, Dec 29, 2015 at 4:15 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Mon, Dec 28, 2015 at 9:09 PM, Shulgin, Oleksandr
<oleksandr.shulgin@zalando.de> wrote:

Still this requires a revert of the memory context handling commit for
load_hba() and load_ident(). I think you can get around the problem by
changing these functions to work with CurrentMemoryContext and set it
explicitly to the newly allocated PostmasterContext in
PerformAuthentication(). In your function you could then create a

temporary

context to be discarded before leaving the function.

Thanks for the review. I didn't understand your point clearly.

In the attached patch, load_hba uses PostmasterContext if it is present,
otherwise CurretMemoryContext. PostmasterContext is present only
in the backend start phase.

I still think you should not try to re-implement check_hba(), but extend
this function with means to report line skip reasons as per your
requirements. Having an optional callback function might be a good fit

(a

possible use case is logging the reasons line by line).

check_hba function is enhanced to fill the hba line details with
reason for mismatch.
In check_hba function whenever a mismatch is found, the fill_hbaline
function is
called to frame the tuple and inserted into tuple store.

This is close enough, but what I actually mean by "a callback" is more or
less like the attached version.

While at it, I've also added some trivial code to preserve keyword quoting
in database and user fields, as well as added netmask output parameter;
also documentation is extended a little.

The biggest question for me is the proper handling of memory contexts for
HBA and ident files data. I think it makes sense to release them
explicitly because with the current state of affairs, we have dangling
pointers in parsed_{hba,ident}_{context,lines} after release of
PostmasterContext. The detailed comment in postgres.c
around MemoryContextDelete(PostmasterContext); suggests that it's not easy
already to keep track of the all things that might be affected by this
cleanup step:

/*
* If the PostmasterContext is still around, recycle the space; we don't
* need it anymore after InitPostgres completes. Note this does not trash
* *MyProcPort, because ConnCreate() allocated that space with malloc()
* ... else we'd need to copy the Port data first. Also, subsidiary data
* such as the username isn't lost either; see ProcessStartupPacket().
*/

Not sure if we need any advanced machinery here like some sort of cleanup
hooks list? For now I've added discard_{hba,ident}() functions and call
them explicitly where appropriate.

--
Alex

Attachments:

pg_hba_lookup_poc_v10.patchtext/x-patch; charset=US-ASCII; name=pg_hba_lookup_poc_v10.patchDownload
From 56b584c25b952d6855d2a3d77eb40755d982efb9 Mon Sep 17 00:00:00 2001
From: Oleksandr Shulgin <oleksandr.shulgin@zalando.de>
Date: Tue, 29 Dec 2015 14:51:27 +0100
Subject: [PATCH] WIP: pg_hba_lookup()

---
 doc/src/sgml/func.sgml               |  39 +++
 src/backend/catalog/system_views.sql |   9 +
 src/backend/libpq/hba.c              | 663 ++++++++++++++++++++++++++++++++++-
 src/backend/utils/init/postinit.c    |  26 +-
 src/include/catalog/pg_proc.h        |   2 +
 src/include/libpq/hba.h              |   4 +
 src/include/utils/builtins.h         |   3 +
 7 files changed, 720 insertions(+), 26 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
new file mode 100644
index e08bf60..2594dd5
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** SELECT collation for ('foo' COLLATE "de_
*** 16576,16581 ****
--- 16576,16594 ----
         <entry><type>text</type></entry>
         <entry>set parameter and return new value</entry>
        </row>
+       <row>
+        <entry>
+         <indexterm>
+          <primary>pg_hba_lookup</primary>
+         </indexterm>
+         <literal><function>pg_hba_lookup(<parameter>database</> <type>text</>,
+                             <parameter>user_name</> <type>text</>
+                             [, <parameter>address</> <type>text</>]
+                             [, <parameter>ssl_inuse</> <type>text</>)</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>scan <filename>pg_hba.conf</> and return examined entries up to a matching one</entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
*************** SELECT set_config('log_statement_stats',
*** 16633,16638 ****
--- 16646,16677 ----
  </programlisting>
     </para>
  
+    <para>
+     <function>pg_hba_lookup</function> returns a set of records containing the
+     line number, mode, type, database, user_name, address, netmask, hostname,
+     method, options and skip reason. For example, to debug problems with user
+     <literal>kommih</> trying to connect to a database <literal>postgres</>
+     from IPv6-address <literal>::1</>, one can issue a following query:
+ <programlisting>
+ postgres=# SELECT * FROM pg_hba_lookup('postgres', 'kommih', '::1');
+  line_number |  mode   | type  | database | user_name |  address  |                 netmask                 | hostname | method | options |          reason          
+ -------------+---------+-------+----------+-----------+-----------+-----------------------------------------+----------+--------+---------+--------------------------
+           84 | skipped | local | {all}    | {all}     |           |                                         |          | trust  | {}      | connection type mismatch
+           86 | skipped | host  | {all}    | {all}     | 127.0.0.1 | 255.255.255.255                         |          | trust  | {}      | IP address mismatch
+           88 | matched | host  | {all}    | {all}     | ::1       | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff |          | trust  | {}      | 
+ (3 rows)
+ 
+ </programlisting>
+     This function actually loads the contents of <filename>pg_hba.conf</> file
+     into memory to perform matching, thus a database administrator can use it
+     to test the effects of changes made to the file in isolation prior to
+     actually applying the configuration cluster-wide with a signal to the
+     postmaster process.
+ 
+     Only superusers can access this function to look the
+     <filename>pg_hba.conf</> entries up.
+    </para>
+    
    </sect2>
  
    <sect2 id="functions-admin-signal">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
new file mode 100644
index 536c805..9ffc4fa
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
*************** RETURNS jsonb
*** 948,950 ****
--- 948,959 ----
  LANGUAGE INTERNAL
  STRICT IMMUTABLE
  AS 'jsonb_set';
+ 
+ CREATE OR REPLACE FUNCTION pg_hba_lookup(IN database text, IN user_name text,
+     IN address text DEFAULT NULL, IN ssl_inuse boolean DEFAULT false,
+ 	OUT line_number int, OUT mode text, OUT type text, OUT database text[],
+ 	OUT user_name text[], OUT address inet, OUT netmask inet,
+         OUT hostname text, OUT method text, OUT options jsonb, OUT reason text)
+ RETURNS SETOF RECORD
+ LANGUAGE INTERNAL
+ AS 'pg_hba_lookup';
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
new file mode 100644
index 94f7cfa..09e819c
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 25,39 ****
--- 25,46 ----
  #include <arpa/inet.h>
  #include <unistd.h>
  
+ #include "access/htup_details.h"
+ #include "catalog/objectaddress.h"
  #include "catalog/pg_collation.h"
+ #include "catalog/pg_type.h"
+ #include "funcapi.h"
  #include "libpq/ip.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/jsonb.h"
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  
***************
*** 52,57 ****
--- 59,66 ----
  #define MAX_TOKEN	256
  #define MAX_LINE	8192
  
+ #define NUM_PG_HBA_LOOKUP_ATTS   11
+ 
  /* callback data for check_network_callback */
  typedef struct check_network_data
  {
*************** typedef struct HbaToken
*** 74,79 ****
--- 83,100 ----
  	bool		quoted;
  } HbaToken;
  
+ /* Optional callback function type for check_hba(). */
+ typedef void (*check_hba_line_callback)(void *context, HbaLine *hba_line,
+ 			  const char *reason);
+ 
+ /* Context to use with lookup_hba_line_callback(). */
+ typedef struct {
+ 	MemoryContext		memcxt;
+ 	TupleDesc			tupdesc;
+ 	Tuplestorestate	   *tuple_store;
+ } LookupHbalineContext;
+ 
+ 
  /*
   * pre-parsed content of HBA config file: list of HbaLine structs.
   * parsed_hba_context is the memory context where it lives.
*************** static List *tokenize_inc_file(List *tok
*** 99,104 ****
--- 120,132 ----
  				  const char *inc_filename);
  static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
  				   int line_num);
+ static Datum getauthmethod(UserAuth auth_method);
+ static void hba_add_jsonb_string_key(JsonbParseState **pstate, char *string_key);
+ static void hba_add_jsonb_bool_value(JsonbParseState **pstate, bool bool_val);
+ static void hba_add_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val);
+ static void hba_add_jsonb_string_value(JsonbParseState **pstate, char *string_value);
+ static Jsonb *gethba_options(HbaLine *hba);
+ static void lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason);
  
  /*
   * isblank() exists in the ISO C99 spec, but it's not very portable yet,
*************** parse_hba_auth_opt(char *name, char *val
*** 1640,1646 ****
   *	request.
   */
  static void
! check_hba(hbaPort *port)
  {
  	Oid			roleid;
  	ListCell   *line;
--- 1668,1675 ----
   *	request.
   */
  static void
! check_hba(hbaPort *port, check_hba_line_callback callback,
! 		  void *callback_context)
  {
  	Oid			roleid;
  	ListCell   *line;
*************** check_hba(hbaPort *port)
*** 1657,1681 ****
--- 1686,1726 ----
  		if (hba->conntype == ctLocal)
  		{
  			if (!IS_AF_UNIX(port->raddr.addr.ss_family))
+ 			{
+ 				if (callback)
+ 					callback(callback_context, hba, "connection type mismatch");
  				continue;
+ 			}
  		}
  		else
  		{
  			if (IS_AF_UNIX(port->raddr.addr.ss_family))
+ 			{
+ 				if (callback)
+ 					callback(callback_context, hba, "connection type mismatch");
  				continue;
+ 			}
  
  			/* Check SSL state */
  			if (port->ssl_in_use)
  			{
  				/* Connection is SSL, match both "host" and "hostssl" */
  				if (hba->conntype == ctHostNoSSL)
+ 				{
+ 					if (callback)
+ 						callback(callback_context, hba, "connection type mismatch");
  					continue;
+ 				}
  			}
  			else
  			{
  				/* Connection is not SSL, match both "host" and "hostnossl" */
  				if (hba->conntype == ctHostSSL)
+ 				{
+ 					if (callback)
+ 						callback(callback_context, hba, "connection type mismatch");
  					continue;
+ 				}
  			}
  
  			/* Check IP address */
*************** check_hba(hbaPort *port)
*** 1686,1699 ****
--- 1731,1752 ----
  					{
  						if (!check_hostname(port,
  											hba->hostname))
+ 						{
+ 							if (callback)
+ 								callback(callback_context, hba, "hostname mismatch");
  							continue;
+ 						}
  					}
  					else
  					{
  						if (!check_ip(&port->raddr,
  									  (struct sockaddr *) & hba->addr,
  									  (struct sockaddr *) & hba->mask))
+ 						{
+ 							if (callback)
+ 								callback(callback_context, hba, "IP address mismatch");
  							continue;
+ 						}
  					}
  					break;
  				case ipCmpAll:
*************** check_hba(hbaPort *port)
*** 1702,1708 ****
--- 1755,1765 ----
  				case ipCmpSameNet:
  					if (!check_same_host_or_net(&port->raddr,
  												hba->ip_cmp_method))
+ 					{
+ 						if (callback)
+ 							callback(callback_context, hba, "samehost/samenet mismatch");
  						continue;
+ 					}
  					break;
  				default:
  					/* shouldn't get here, but deem it no-match if so */
*************** check_hba(hbaPort *port)
*** 1713,1722 ****
--- 1770,1790 ----
  		/* Check database and role */
  		if (!check_db(port->database_name, port->user_name, roleid,
  					  hba->databases))
+ 		{
+ 			if (callback)
+ 				callback(callback_context, hba, "database name mismatch");
  			continue;
+ 		}
  
  		if (!check_role(port->user_name, roleid, hba->roles))
+ 		{
+ 			if (callback)
+ 				callback(callback_context, hba, "user name mismatch");
  			continue;
+ 		}
+ 
+ 		if (callback)
+ 			callback(callback_context, hba, NULL);
  
  		/* Found a record that matched! */
  		port->hba = hba;
*************** load_hba(void)
*** 1770,1777 ****
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	Assert(PostmasterContext);
! 	hbacxt = AllocSetContextCreate(PostmasterContext,
  								   "hba parser context",
  								   ALLOCSET_DEFAULT_MINSIZE,
  								   ALLOCSET_DEFAULT_MINSIZE,
--- 1838,1844 ----
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	hbacxt = AllocSetContextCreate(CurrentMemoryContext,
  								   "hba parser context",
  								   ALLOCSET_DEFAULT_MINSIZE,
  								   ALLOCSET_DEFAULT_MINSIZE,
*************** load_hba(void)
*** 1829,1842 ****
  	}
  
  	/* Loaded new file successfully, replace the one we use */
! 	if (parsed_hba_context != NULL)
! 		MemoryContextDelete(parsed_hba_context);
  	parsed_hba_context = hbacxt;
  	parsed_hba_lines = new_parsed_lines;
  
  	return true;
  }
  
  /*
   * Parse one tokenised line from the ident config file and store the result in
   * an IdentLine structure, or NULL if parsing fails.
--- 1896,1921 ----
  	}
  
  	/* Loaded new file successfully, replace the one we use */
! 	discard_hba();
  	parsed_hba_context = hbacxt;
  	parsed_hba_lines = new_parsed_lines;
  
  	return true;
  }
  
+ 
+ void
+ discard_hba(void)
+ {
+ 	if (parsed_hba_context != NULL)
+ 	{
+ 		MemoryContextDelete(parsed_hba_context);
+ 		parsed_hba_context = NULL;
+ 		parsed_hba_lines = NIL;
+ 	}
+ }
+ 
+ 
  /*
   * Parse one tokenised line from the ident config file and store the result in
   * an IdentLine structure, or NULL if parsing fails.
*************** load_ident(void)
*** 2148,2155 ****
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	Assert(PostmasterContext);
! 	ident_context = AllocSetContextCreate(PostmasterContext,
  										  "ident parser context",
  										  ALLOCSET_DEFAULT_MINSIZE,
  										  ALLOCSET_DEFAULT_MINSIZE,
--- 2227,2233 ----
  	FreeFile(file);
  
  	/* Now parse all the lines */
! 	ident_context = AllocSetContextCreate(CurrentMemoryContext,
  										  "ident parser context",
  										  ALLOCSET_DEFAULT_MINSIZE,
  										  ALLOCSET_DEFAULT_MINSIZE,
*************** load_ident(void)
*** 2202,2207 ****
--- 2280,2296 ----
  	}
  
  	/* Loaded new file successfully, replace the one we use */
+ 	discard_ident();
+ 	parsed_ident_context = ident_context;
+ 	parsed_ident_lines = new_parsed_lines;
+ 
+ 	return true;
+ }
+ 
+ 
+ void
+ discard_ident(void)
+ {
  	if (parsed_ident_lines != NIL)
  	{
  		foreach(parsed_line_cell, parsed_ident_lines)
*************** load_ident(void)
*** 2210,2227 ****
  			if (newline->ident_user[0] == '/')
  				pg_regfree(&newline->re);
  		}
  	}
  	if (parsed_ident_context != NULL)
  		MemoryContextDelete(parsed_ident_context);
! 
! 	parsed_ident_context = ident_context;
! 	parsed_ident_lines = new_parsed_lines;
! 
! 	return true;
  }
  
  
- 
  /*
   *	Determine what authentication method should be used when accessing database
   *	"database" from frontend "raddr", user "user".  Return the method and
--- 2299,2314 ----
  			if (newline->ident_user[0] == '/')
  				pg_regfree(&newline->re);
  		}
+ 		parsed_ident_lines = NIL;
  	}
  	if (parsed_ident_context != NULL)
+ 	{
  		MemoryContextDelete(parsed_ident_context);
! 		parsed_ident_context = NULL;
! 	}
  }
  
  
  /*
   *	Determine what authentication method should be used when accessing database
   *	"database" from frontend "raddr", user "user".  Return the method and
*************** load_ident(void)
*** 2233,2237 ****
  void
  hba_getauthmethod(hbaPort *port)
  {
! 	check_hba(port);
  }
--- 2320,2872 ----
  void
  hba_getauthmethod(hbaPort *port)
  {
! 	check_hba(port, NULL, NULL);
! }
! 
! 
! /*
!  * 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 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 uaLDAP:
! 			result = CStringGetTextDatum("ldap");
! 			break;
! 		case uaCert:
! 			result = CStringGetTextDatum("cert");
! 			break;
! 		case uaRADIUS:
! 			result = CStringGetTextDatum("radius");
! 			break;
! 		case uaPeer:
! 			result = CStringGetTextDatum("peer");
! 			break;
! 		default:
! 			elog(ERROR, "unexpected authentication method in parsed HBA entry");
! 			break;
! 	}
! 
! 	return result;
! }
! 
! static void
! hba_add_jsonb_string_key(JsonbParseState **pstate, char *string_key)
! {
! 	JsonbValue	jb;
! 
! 	jb.type = jbvString;
! 	jb.val.string.len = strlen(string_key);
! 	jb.val.string.val = pstrdup(string_key);
! 	pushJsonbValue(pstate, WJB_KEY, &jb);
! }
! 
! static void
! hba_add_jsonb_bool_value(JsonbParseState **pstate, bool bool_val)
! {
! 	JsonbValue	jb;
! 
! 	jb.type = jbvBool;
! 	jb.val.boolean = bool_val;
! 
! 	pushJsonbValue(pstate, WJB_VALUE, &jb);
! }
! 
! static void
! hba_add_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val)
! {
! 	JsonbValue	jb;
! 	char		outputstr[64];
! 
! 	snprintf(outputstr, 64, "%d", int32_val);
! 	jb.type = jbvNumeric;
! 	jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1));
! 
! 	pushJsonbValue(pstate, WJB_VALUE, &jb);
! }
! 
! static void
! hba_add_jsonb_string_value(JsonbParseState **pstate, char *string_value)
! {
! 	JsonbValue	jb;
! 
! 	jb.type = jbvString;
! 	jb.val.string.len = strlen(string_value);
! 	jb.val.string.val = pstrdup(string_value);
! 	pushJsonbValue(pstate, WJB_VALUE, &jb);
! }
! 
! static Jsonb *
! gethba_options(HbaLine *hba)
! {
! 	JsonbParseState	   *parseState = NULL;
! 	JsonbValue		   *result;
! 
! 	result = pushJsonbValue(&parseState, WJB_BEGIN_OBJECT, NULL);
! 
! 	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
! 	{
! 		if (hba->include_realm)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "include_realm");
! 			hba_add_jsonb_bool_value(&parseState, true);
! 		}
! 
! 		if (hba->krb_realm)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "krb_realm");
! 			hba_add_jsonb_string_value(&parseState, hba->krb_realm);
! 		}
! 	}
! 
! 	if (hba->usermap)
! 	{
! 		hba_add_jsonb_string_key(&parseState, "map");
! 		hba_add_jsonb_string_value(&parseState, hba->usermap);
! 	}
! 
! 	if (hba->clientcert)
! 	{
! 		hba_add_jsonb_string_key(&parseState, "clientcert");
! 		hba_add_jsonb_bool_value(&parseState, true);
! 	}
! 
! 	if (hba->pamservice)
! 	{
! 		hba_add_jsonb_string_key(&parseState, "pamservice");
! 		hba_add_jsonb_string_value(&parseState, hba->pamservice);
! 	}
! 
! 	if (hba->auth_method == uaLDAP)
! 	{
! 		if (hba->ldapserver)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapserver");
! 			hba_add_jsonb_string_value(&parseState, hba->ldapserver);
! 		}
! 
! 		if (hba->ldapport)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapport");
! 			hba_add_jsonb_int32_value(&parseState, hba->ldapport);
! 		}
! 
! 		if (hba->ldaptls)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldaptls");
! 			hba_add_jsonb_bool_value(&parseState, true);
! 		}
! 
! 		if (hba->ldapprefix)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapprefix");
! 			hba_add_jsonb_string_value(&parseState, hba->ldapprefix);
! 		}
! 
! 		if (hba->ldapsuffix)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapsuffix");
! 			hba_add_jsonb_string_value(&parseState, hba->ldapsuffix);
! 		}
! 
! 		if (hba->ldapbasedn)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapbasedn");
! 			hba_add_jsonb_string_value(&parseState, hba->ldapbasedn);
! 		}
! 
! 		if (hba->ldapbinddn)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapbinddn");
! 			hba_add_jsonb_string_value(&parseState, hba->ldapbinddn);
! 		}
! 
! 		if (hba->ldapbindpasswd)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapbindpasswd");
! 			hba_add_jsonb_string_value(&parseState, hba->ldapbindpasswd);
! 		}
! 
! 		if (hba->ldapsearchattribute)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapsearchattribute");
! 			hba_add_jsonb_string_value(&parseState, hba->ldapsearchattribute);
! 		}
! 
! 		if (hba->ldapscope)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "ldapscope");
! 			hba_add_jsonb_int32_value(&parseState, hba->ldapscope);
! 		}
! 	}
! 
! 	if (hba->auth_method == uaRADIUS)
! 	{
! 		if (hba->radiusserver)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "radiusserver");
! 			hba_add_jsonb_string_value(&parseState, hba->radiusserver);
! 		}
! 
! 		if (hba->radiussecret)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "radiussecret");
! 			hba_add_jsonb_string_value(&parseState, hba->radiussecret);
! 		}
! 
! 		if (hba->radiusidentifier)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "radiusidentifier");
! 			hba_add_jsonb_string_value(&parseState, hba->radiusidentifier);
! 		}
! 
! 		if (hba->radiusport)
! 		{
! 			hba_add_jsonb_string_key(&parseState, "radiusport");
! 			hba_add_jsonb_int32_value(&parseState, hba->radiusport);
! 		}
! 	}
! 
! 	result = pushJsonbValue(&parseState, WJB_END_OBJECT, NULL);
! 	return JsonbValueToJsonb(result);
! }
! 
! 
! static void
! lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason)
! {
! 	Datum			values[NUM_PG_HBA_LOOKUP_ATTS];
! 	bool			nulls[NUM_PG_HBA_LOOKUP_ATTS];
! 	ListCell	   *dbcell;
! 	char			buffer[NI_MAXHOST];
! 	HeapTuple		tuple;
! 	int				index;
! 	MemoryContext	old_cxt;
! 	LookupHbalineContext   *mycxt;
! 
! 	mycxt = (LookupHbalineContext *) context;
! 
! 	index = 0;
! 	memset(values, 0, sizeof(values));
! 	memset(nulls, 0, sizeof(nulls));
! 
! 	old_cxt = MemoryContextSwitchTo(mycxt->memcxt);
! 
! 	/* line_number */
! 	values[index] = Int32GetDatum(hba->linenumber);
! 
! 	/* mode */
! 	index++;
! 	if (reason == NULL)
! 		values[index] = CStringGetTextDatum("matched");
! 	else
! 		values[index] = CStringGetTextDatum("skipped");
! 
! 	/* 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;
! 		default:
! 			elog(ERROR, "unexpected connection type in parsed HBA entry");
! 			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->quoted ? psprintf("\"%s\"", tok->string) : tok->string);
! 		}
! 
! 		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->quoted ? psprintf("\"%s\"", tok->string) : tok->string);
! 		}
! 
! 		values[index] = PointerGetDatum(strlist_to_textarray(roles));
! 	}
! 	else
! 		nulls[index] = true;
! 
! 	/* address */
! 	index++;
! 	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
! 	}
! 	else
! 		nulls[index] = true;
! 
! 	/* netmask */
! 	index++;
! 	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
! 	}
! 	else
! 		nulls[index] = true;
! 
! 	/* hostname */
! 	index++;
! 	if (hba->hostname)
! 		values[index] = CStringGetTextDatum(hba->hostname);
! 	else
! 		nulls[index] = true;
! 
! 	/* method */
! 	index++;
! 	values[index] = getauthmethod(hba->auth_method);
! 
! 	/* options */
! 	index++;
! 	values[index] = PointerGetDatum(gethba_options(hba));
! 
! 	/* reason */
! 	index++;
! 	if (reason)
! 		values[index] = CStringGetTextDatum(reason);
! 	else
! 		nulls[index] = true;
! 
! 	tuple = heap_form_tuple(mycxt->tupdesc, values, nulls);
! 	tuplestore_puttuple(mycxt->tuple_store, tuple);
! 
! 	MemoryContextSwitchTo(old_cxt);
! 	return;
! }
! 
! /*
!  * SQL-accessible SRF to return all the settings from the pg_hba.conf
!  * file.
!  */
! Datum
! pg_hba_lookup(PG_FUNCTION_ARGS)
! {
! 	hbaPort				   *port;
! 	Tuplestorestate		   *tuple_store;
! 	TupleDesc				tupdesc;
! 	MemoryContext			old_cxt;
! 	LookupHbalineContext   *mycxt;
! 	ReturnSetInfo		   *rsi;
! 
! 	if (!superuser())
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser to view pg_hba.conf settings"))));
! 
! 	/*
! 	 * 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;
! 
! 	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
! 		(rsi->allowedModes & SFRM_Materialize) == 0)
! 		ereport(ERROR,
! 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 				 errmsg("set-valued function called in context that cannot accept a set")));
! 
! 	port = palloc0(sizeof(hbaPort));
! 
! 	if (PG_ARGISNULL(0))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
! 				 (errmsg("database name is required to match pg_hba configuration entry"))));
! 	else
! 		port->database_name = TextDatumGetCString(PG_GETARG_DATUM(0));
! 
! 	if (PG_ARGISNULL(1))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
! 				 (errmsg("user name is required to match pg_hba configuration entry"))));
! 	else
! 		port->user_name = TextDatumGetCString(PG_GETARG_DATUM(1));
! 
! 
! 	if (!PG_ARGISNULL(2))
! 	{
! 		char	   *address = NULL;
! 		struct addrinfo *gai_result = NULL;
! 		struct addrinfo hints;
! 		int			ret;
! 
! 		address = TextDatumGetCString(PG_GETARG_DATUM(2));
! 
! 		/* Get the IP address either way */
! 		hints.ai_flags = AI_NUMERICHOST;
! 		hints.ai_family = AF_UNSPEC;
! 		hints.ai_socktype = 0;
! 		hints.ai_protocol = 0;
! 		hints.ai_addrlen = 0;
! 		hints.ai_canonname = NULL;
! 		hints.ai_addr = NULL;
! 		hints.ai_next = NULL;
! 
! 		ret = getaddrinfo(address, NULL, &hints, &gai_result);
! 		if (ret == 0 && gai_result)
! 			memcpy(&port->raddr.addr, gai_result->ai_addr, gai_result->ai_addrlen);
! 		else if (ret == EAI_NONAME)
! 		{
! 			struct addrinfo *gai_result2 = NULL;
! 
! 			port->remote_hostname = pstrdup(address);
! 
! 			ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result2);
! 			if (ret == 0 && gai_result2)
! 				memcpy(&port->raddr.addr, gai_result2->ai_addr, gai_result2->ai_addrlen);
! 			else
! 				ereport(ERROR,
! 						(errmsg("getaddrinfo failed to look into hostname \"%s\": %s",
! 								port->remote_hostname, gai_strerror(ret))));
! 
! 			if (gai_result2)
! 				freeaddrinfo(gai_result2);
! 		}
! 		else
! 			ereport(ERROR,
! 					(errmsg("invalid IP address \"%s\": %s",
! 							address, gai_strerror(ret))));
! 		if (gai_result)
! 			freeaddrinfo(gai_result);
! 	}
! 	else
! 		port->raddr.addr.ss_family = AF_UNIX;
! 
! 	port->ssl_in_use = DatumGetBool(PG_GETARG_DATUM(3));
! 
! 	rsi->returnMode = SFRM_Materialize;
! 
! 	if (!load_hba())
! 		ereport(ERROR,
! 				(errmsg("Failed to load pg_hba.conf file"),
! 				 errhint("More details may be available in the server log.")));
! 
! 	/*
! 	 * Create the tupledesc and tuplestore in the per_query context as
! 	 * required for SFRM_Materialize.
! 	 */
! 	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
! 
! 	tupdesc = CreateTemplateTupleDesc(NUM_PG_HBA_LOOKUP_ATTS, false);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "line_number",
! 					   INT4OID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "mode",
! 					   TEXTOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "type",
! 					   TEXTOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "database",
! 					   TEXTARRAYOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "user_name",
! 					   TEXTARRAYOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "address",
! 					   INETOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "netmask",
! 					   INETOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "hostname",
! 					   TEXTOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "method",
! 					   TEXTOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "options",
! 					   JSONBOID, -1, 0);
! 	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "reason",
! 					   TEXTOID, -1, 0);
! 	BlessTupleDesc(tupdesc);
! 
! 	tuple_store =
! 		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
! 							  false, work_mem);
! 
! 	MemoryContextSwitchTo(old_cxt);
! 
! 	mycxt = (LookupHbalineContext *) palloc(sizeof(LookupHbalineContext));
! 	mycxt->memcxt = AllocSetContextCreate(CurrentMemoryContext,
! 										  "pg_hba_lookup tuple cxt",
! 										  ALLOCSET_DEFAULT_MINSIZE,
! 										  ALLOCSET_DEFAULT_INITSIZE,
! 										  ALLOCSET_DEFAULT_MAXSIZE);
! 	mycxt->tupdesc = tupdesc;
! 	mycxt->tuple_store = tuple_store;
! 
! 	check_hba(port, lookup_hba_line_callback, mycxt);
! 
! 	MemoryContextDelete(mycxt->memcxt);
! 	pfree(mycxt);
! 
! 	/* Clean up the memory allocated by load_hba() explicitly. */
! 	discard_hba();
! 
! 	rsi->setDesc = tupdesc;
! 	rsi->setResult = tuple_store;
! 
! 	PG_RETURN_NULL();
  }
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
new file mode 100644
index 7b19714..12427b7
*** a/src/backend/utils/init/postinit.c
--- b/src/backend/utils/init/postinit.c
*************** PerformAuthentication(Port *port)
*** 191,208 ****
  	 * FIXME: [fork/exec] Ugh.  Is there a way around this overhead?
  	 */
  #ifdef EXEC_BACKEND
- 	/*
- 	 * load_hba() and load_ident() want to work within the PostmasterContext,
- 	 * so create that if it doesn't exist (which it won't).  We'll delete it
- 	 * again later, in PostgresMain.
- 	 */
- 	if (PostmasterContext == NULL)
- 		PostmasterContext = AllocSetContextCreate(TopMemoryContext,
- 												  "Postmaster",
- 												  ALLOCSET_DEFAULT_MINSIZE,
- 												  ALLOCSET_DEFAULT_INITSIZE,
- 												  ALLOCSET_DEFAULT_MAXSIZE);
- 
  	if (!load_hba())
  	{
  		/*
--- 191,196 ----
*************** PerformAuthentication(Port *port)
*** 241,246 ****
--- 229,240 ----
  	 */
  	disable_timeout(STATEMENT_TIMEOUT, false);
  
+ #ifdef EXEC_BACKEND
+ 	/* Discard the loaded HBA and ident files data explicitly. */
+ 	discard_hba();
+ 	discard_ident();
+ #endif
+ 
  	if (Log_connections)
  	{
  		if (am_walsender)
*************** InitPostgres(const char *in_dbname, Oid
*** 736,741 ****
--- 730,743 ----
  	}
  
  	/*
+ 	 * We don't need the HBA and ident data going forward, but we can't rely
+ 	 * on release of PostmasterContext to clean that up, so discard them
+ 	 * explicitly here.
+ 	 */
+ 	discard_hba();
+ 	discard_ident();
+ 
+ 	/*
  	 * If we're trying to shut down, only superusers can connect, and new
  	 * replication connections are not allowed.
  	 */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
new file mode 100644
index d8640db..ebd96da
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DATA(insert OID = 2084 (  pg_show_all_se
*** 3079,3084 ****
--- 3079,3086 ----
  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 = 3997 (  pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f f t v u 4 0 2249 "25 25 25 16" "{25,25,25,16,23,25,25,1009,1009,869,869,25,25,3802,25}" "{i,i,i,i,o,o,o,o,o,o,o,o,o,o,o}" "{database,user_name,address,ssl_inuse,line_number,mode,type,database,user_name,address,netmask,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup _null_ _null_ _null_));
+ DESCR("view client authentication settings");
  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 = 1065 (  pg_prepared_xact PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{28,25,1184,26,26}" "{o,o,o,o,o}" "{transaction,gid,prepared,ownerid,dbid}" _null_ _null_ pg_prepared_xact _null_ _null_ _null_ ));
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
new file mode 100644
index 68a953a..291300b
*** a/src/include/libpq/hba.h
--- b/src/include/libpq/hba.h
*************** typedef struct Port hbaPort;
*** 98,103 ****
--- 98,107 ----
  
  extern bool load_hba(void);
  extern bool load_ident(void);
+ 
+ extern void discard_hba(void);
+ extern void discard_ident(void);
+ 
  extern void hba_getauthmethod(hbaPort *port);
  extern int check_usermap(const char *usermap_name,
  			  const char *pg_role, const char *auth_user,
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
new file mode 100644
index e610bf3..2d60879
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum set_config_by_name(PG_FUNCT
*** 1123,1128 ****
--- 1123,1131 ----
  extern Datum show_all_settings(PG_FUNCTION_ARGS);
  extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
  
+ /* hba.c */
+ extern Datum pg_hba_lookup(PG_FUNCTION_ARGS);
+ 
  /* rls.c */
  extern Datum row_security_active(PG_FUNCTION_ARGS);
  extern Datum row_security_active_name(PG_FUNCTION_ARGS);
-- 
2.5.0

#47Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Shulgin, Oleksandr (#46)
2 attachment(s)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Wed, Dec 30, 2015 at 1:07 AM, Shulgin, Oleksandr
<oleksandr.shulgin@zalando.de> wrote:

This is close enough, but what I actually mean by "a callback" is more or
less like the attached version.

Thanks for the changes.

While at it, I've also added some trivial code to preserve keyword quoting
in database and user fields, as well as added netmask output parameter; also
documentation is extended a little.

Thanks for the documentation changes and regarding the quoting, in any system
catalog table, the quoted objects are represented without quotes as below.

postgres=> select datname from pg_database;
datname
---------------
postgres
template1
template0
test_user2_db
TEST_USER1_DB
test_user2_dB
(6 rows)

Adding quotes to pg_hba_lookup function makes it different from others.
The issues regarding the same is already discussed in [1]/messages/by-id/CAFj8pRARzDScocMK30gyYdOgiwuTUcZ7EvE-bBg+wV2Wg5EQRQ@mail.gmail.com.

select a.database[1]/messages/by-id/CAFj8pRARzDScocMK30gyYdOgiwuTUcZ7EvE-bBg+wV2Wg5EQRQ@mail.gmail.com, b.datname from pg_hba_lookup('postgres','kommih','::1')
as a, pg_database as b where a.database[1]/messages/by-id/CAFj8pRARzDScocMK30gyYdOgiwuTUcZ7EvE-bBg+wV2Wg5EQRQ@mail.gmail.com
= b.datname;

The queries like above are not possible with quoted output. It is very
rare that the
pg_hba_lookup function used in join operations, but still it is better
to provide
data without quotes. so I reverted these changes in the attached latest patch.

The biggest question for me is the proper handling of memory contexts for
HBA and ident files data. I think it makes sense to release them explicitly
because with the current state of affairs, we have dangling pointers in
parsed_{hba,ident}_{context,lines} after release of PostmasterContext. The
detailed comment in postgres.c around
MemoryContextDelete(PostmasterContext); suggests that it's not easy already
to keep track of the all things that might be affected by this cleanup step:

/*
* If the PostmasterContext is still around, recycle the space; we don't
* need it anymore after InitPostgres completes. Note this does not trash
* *MyProcPort, because ConnCreate() allocated that space with malloc()
* ... else we'd need to copy the Port data first. Also, subsidiary data
* such as the username isn't lost either; see ProcessStartupPacket().
*/

Not sure if we need any advanced machinery here like some sort of cleanup
hooks list? For now I've added discard_{hba,ident}() functions and call
them explicitly where appropriate.

The added functions properly frees the hba and ident contexts once its use is
finished. I removed the discard function calls in PerformAuthentication function
in EXEC_BACKEND mode, as these are called once the PerformAuthenication
function finishes.

The discard hba and ident context patch is separated from
pg_hba_lookup patch for
easier understanding. The pg_hba_lookup patch needs to be applied on top of
discard_hba_and_ident_cxt patch.

[1]: /messages/by-id/CAFj8pRARzDScocMK30gyYdOgiwuTUcZ7EvE-bBg+wV2Wg5EQRQ@mail.gmail.com

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_hba_lookup_poc_v11.patchapplication/octet-stream; name=pg_hba_lookup_poc_v11.patchDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 8ef9fce..215c7c0 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -16576,6 +16576,19 @@ SELECT collation for ('foo' COLLATE "de_DE");
        <entry><type>text</type></entry>
        <entry>set parameter and return new value</entry>
       </row>
+      <row>
+       <entry>
+        <indexterm>
+         <primary>pg_hba_lookup</primary>
+        </indexterm>
+        <literal><function>pg_hba_lookup(<parameter>database</> <type>text</>,
+                            <parameter>user_name</> <type>text</>
+                            [, <parameter>address</> <type>text</>]
+                            [, <parameter>ssl_inuse</> <type>text</>)</function></literal>
+       </entry>
+       <entry><type>record</type></entry>
+       <entry>scan <filename>pg_hba.conf</> and return examined entries up to a matching one</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
@@ -16633,6 +16646,32 @@ SELECT set_config('log_statement_stats', 'off', false);
 </programlisting>
    </para>
 
+   <para>
+    <function>pg_hba_lookup</function> returns a set of records containing the
+    line number, mode, type, database, user_name, address, netmask, hostname,
+    method, options and skip reason. For example, to debug problems with user
+    <literal>kommih</> trying to connect to a database <literal>postgres</>
+    from IPv6-address <literal>::1</>, one can issue a following query:
+<programlisting>
+postgres=# SELECT * FROM pg_hba_lookup('postgres', 'kommih', '::1');
+ line_number |  mode   | type  | database | user_name |  address  |                 netmask                 | hostname | method | options |          reason          
+-------------+---------+-------+----------+-----------+-----------+-----------------------------------------+----------+--------+---------+--------------------------
+          84 | skipped | local | {all}    | {all}     |           |                                         |          | trust  | {}      | connection type mismatch
+          86 | skipped | host  | {all}    | {all}     | 127.0.0.1 | 255.255.255.255                         |          | trust  | {}      | IP address mismatch
+          88 | matched | host  | {all}    | {all}     | ::1       | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff |          | trust  | {}      | 
+(3 rows)
+
+</programlisting>
+    This function actually loads the contents of <filename>pg_hba.conf</> file
+    into memory to perform matching, thus a database administrator can use it
+    to test the effects of changes made to the file in isolation prior to
+    actually applying the configuration cluster-wide with a signal to the
+    postmaster process.
+
+    Only superusers can access this function to look the
+    <filename>pg_hba.conf</> entries up.
+   </para>
+   
   </sect2>
 
   <sect2 id="functions-admin-signal">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 536c805..9ffc4fa 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -948,3 +948,12 @@ RETURNS jsonb
 LANGUAGE INTERNAL
 STRICT IMMUTABLE
 AS 'jsonb_set';
+
+CREATE OR REPLACE FUNCTION pg_hba_lookup(IN database text, IN user_name text,
+    IN address text DEFAULT NULL, IN ssl_inuse boolean DEFAULT false,
+	OUT line_number int, OUT mode text, OUT type text, OUT database text[],
+	OUT user_name text[], OUT address inet, OUT netmask inet,
+        OUT hostname text, OUT method text, OUT options jsonb, OUT reason text)
+RETURNS SETOF RECORD
+LANGUAGE INTERNAL
+AS 'pg_hba_lookup';
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 58a4565..fb1dbe7 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -25,15 +25,22 @@
 #include <arpa/inet.h>
 #include <unistd.h>
 
+#include "access/htup_details.h"
+#include "catalog/objectaddress.h"
 #include "catalog/pg_collation.h"
+#include "catalog/pg_type.h"
+#include "funcapi.h"
 #include "libpq/ip.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/jsonb.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 
@@ -52,6 +59,8 @@
 #define MAX_TOKEN	256
 #define MAX_LINE	8192
 
+#define NUM_PG_HBA_LOOKUP_ATTS   11
+
 /* callback data for check_network_callback */
 typedef struct check_network_data
 {
@@ -74,6 +83,18 @@ typedef struct HbaToken
 	bool		quoted;
 } HbaToken;
 
+/* Optional callback function type for check_hba(). */
+typedef void (*check_hba_line_callback)(void *context, HbaLine *hba_line,
+			  const char *reason);
+
+/* Context to use with lookup_hba_line_callback(). */
+typedef struct {
+	MemoryContext		memcxt;
+	TupleDesc			tupdesc;
+	Tuplestorestate	   *tuple_store;
+} LookupHbalineContext;
+
+
 /*
  * pre-parsed content of HBA config file: list of HbaLine structs.
  * parsed_hba_context is the memory context where it lives.
@@ -99,6 +120,13 @@ 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);
+static Datum getauthmethod(UserAuth auth_method);
+static void hba_add_jsonb_string_key(JsonbParseState **pstate, char *string_key);
+static void hba_add_jsonb_bool_value(JsonbParseState **pstate, bool bool_val);
+static void hba_add_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val);
+static void hba_add_jsonb_string_value(JsonbParseState **pstate, char *string_value);
+static Jsonb *gethba_options(HbaLine *hba);
+static void lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason);
 
 /*
  * isblank() exists in the ISO C99 spec, but it's not very portable yet,
@@ -1640,7 +1668,8 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  *	request.
  */
 static void
-check_hba(hbaPort *port)
+check_hba(hbaPort *port, check_hba_line_callback callback,
+		  void *callback_context)
 {
 	Oid			roleid;
 	ListCell   *line;
@@ -1657,25 +1686,41 @@ check_hba(hbaPort *port)
 		if (hba->conntype == ctLocal)
 		{
 			if (!IS_AF_UNIX(port->raddr.addr.ss_family))
+			{
+				if (callback)
+					callback(callback_context, hba, "connection type mismatch");
 				continue;
+			}
 		}
 		else
 		{
 			if (IS_AF_UNIX(port->raddr.addr.ss_family))
+			{
+				if (callback)
+					callback(callback_context, hba, "connection type mismatch");
 				continue;
+			}
 
 			/* Check SSL state */
 			if (port->ssl_in_use)
 			{
 				/* Connection is SSL, match both "host" and "hostssl" */
 				if (hba->conntype == ctHostNoSSL)
+				{
+					if (callback)
+						callback(callback_context, hba, "connection type mismatch");
 					continue;
+				}
 			}
 			else
 			{
 				/* Connection is not SSL, match both "host" and "hostnossl" */
 				if (hba->conntype == ctHostSSL)
+				{
+					if (callback)
+						callback(callback_context, hba, "connection type mismatch");
 					continue;
+				}
 			}
 
 			/* Check IP address */
@@ -1686,14 +1731,22 @@ check_hba(hbaPort *port)
 					{
 						if (!check_hostname(port,
 											hba->hostname))
+						{
+							if (callback)
+								callback(callback_context, hba, "hostname mismatch");
 							continue;
+						}
 					}
 					else
 					{
 						if (!check_ip(&port->raddr,
 									  (struct sockaddr *) & hba->addr,
 									  (struct sockaddr *) & hba->mask))
+						{
+							if (callback)
+								callback(callback_context, hba, "IP address mismatch");
 							continue;
+						}
 					}
 					break;
 				case ipCmpAll:
@@ -1702,7 +1755,11 @@ check_hba(hbaPort *port)
 				case ipCmpSameNet:
 					if (!check_same_host_or_net(&port->raddr,
 												hba->ip_cmp_method))
+					{
+						if (callback)
+							callback(callback_context, hba, "samehost/samenet mismatch");
 						continue;
+					}
 					break;
 				default:
 					/* shouldn't get here, but deem it no-match if so */
@@ -1713,10 +1770,21 @@ check_hba(hbaPort *port)
 		/* Check database and role */
 		if (!check_db(port->database_name, port->user_name, roleid,
 					  hba->databases))
+		{
+			if (callback)
+				callback(callback_context, hba, "database name mismatch");
 			continue;
+		}
 
 		if (!check_role(port->user_name, roleid, hba->roles))
+		{
+			if (callback)
+				callback(callback_context, hba, "user name mismatch");
 			continue;
+		}
+
+		if (callback)
+			callback(callback_context, hba, NULL);
 
 		/* Found a record that matched! */
 		port->hba = hba;
@@ -2256,5 +2324,551 @@ discard_ident(void)
 void
 hba_getauthmethod(hbaPort *port)
 {
-	check_hba(port);
+	check_hba(port, NULL, NULL);
+}
+
+
+/*
+ * 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 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 uaLDAP:
+			result = CStringGetTextDatum("ldap");
+			break;
+		case uaCert:
+			result = CStringGetTextDatum("cert");
+			break;
+		case uaRADIUS:
+			result = CStringGetTextDatum("radius");
+			break;
+		case uaPeer:
+			result = CStringGetTextDatum("peer");
+			break;
+		default:
+			elog(ERROR, "unexpected authentication method in parsed HBA entry");
+			break;
+	}
+
+	return result;
+}
+
+static void
+hba_add_jsonb_string_key(JsonbParseState **pstate, char *string_key)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvString;
+	jb.val.string.len = strlen(string_key);
+	jb.val.string.val = pstrdup(string_key);
+	pushJsonbValue(pstate, WJB_KEY, &jb);
+}
+
+static void
+hba_add_jsonb_bool_value(JsonbParseState **pstate, bool bool_val)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvBool;
+	jb.val.boolean = bool_val;
+
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
+
+static void
+hba_add_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val)
+{
+	JsonbValue	jb;
+	char		outputstr[64];
+
+	snprintf(outputstr, 64, "%d", int32_val);
+	jb.type = jbvNumeric;
+	jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1));
+
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
+
+static void
+hba_add_jsonb_string_value(JsonbParseState **pstate, char *string_value)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvString;
+	jb.val.string.len = strlen(string_value);
+	jb.val.string.val = pstrdup(string_value);
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
+
+static Jsonb *
+gethba_options(HbaLine *hba)
+{
+	JsonbParseState	   *parseState = NULL;
+	JsonbValue		   *result;
+
+	result = pushJsonbValue(&parseState, WJB_BEGIN_OBJECT, NULL);
+
+	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
+	{
+		if (hba->include_realm)
+		{
+			hba_add_jsonb_string_key(&parseState, "include_realm");
+			hba_add_jsonb_bool_value(&parseState, true);
+		}
+
+		if (hba->krb_realm)
+		{
+			hba_add_jsonb_string_key(&parseState, "krb_realm");
+			hba_add_jsonb_string_value(&parseState, hba->krb_realm);
+		}
+	}
+
+	if (hba->usermap)
+	{
+		hba_add_jsonb_string_key(&parseState, "map");
+		hba_add_jsonb_string_value(&parseState, hba->usermap);
+	}
+
+	if (hba->clientcert)
+	{
+		hba_add_jsonb_string_key(&parseState, "clientcert");
+		hba_add_jsonb_bool_value(&parseState, true);
+	}
+
+	if (hba->pamservice)
+	{
+		hba_add_jsonb_string_key(&parseState, "pamservice");
+		hba_add_jsonb_string_value(&parseState, hba->pamservice);
+	}
+
+	if (hba->auth_method == uaLDAP)
+	{
+		if (hba->ldapserver)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapserver");
+			hba_add_jsonb_string_value(&parseState, hba->ldapserver);
+		}
+
+		if (hba->ldapport)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapport");
+			hba_add_jsonb_int32_value(&parseState, hba->ldapport);
+		}
+
+		if (hba->ldaptls)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldaptls");
+			hba_add_jsonb_bool_value(&parseState, true);
+		}
+
+		if (hba->ldapprefix)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapprefix");
+			hba_add_jsonb_string_value(&parseState, hba->ldapprefix);
+		}
+
+		if (hba->ldapsuffix)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapsuffix");
+			hba_add_jsonb_string_value(&parseState, hba->ldapsuffix);
+		}
+
+		if (hba->ldapbasedn)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapbasedn");
+			hba_add_jsonb_string_value(&parseState, hba->ldapbasedn);
+		}
+
+		if (hba->ldapbinddn)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapbinddn");
+			hba_add_jsonb_string_value(&parseState, hba->ldapbinddn);
+		}
+
+		if (hba->ldapbindpasswd)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapbindpasswd");
+			hba_add_jsonb_string_value(&parseState, hba->ldapbindpasswd);
+		}
+
+		if (hba->ldapsearchattribute)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapsearchattribute");
+			hba_add_jsonb_string_value(&parseState, hba->ldapsearchattribute);
+		}
+
+		if (hba->ldapscope)
+		{
+			hba_add_jsonb_string_key(&parseState, "ldapscope");
+			hba_add_jsonb_int32_value(&parseState, hba->ldapscope);
+		}
+	}
+
+	if (hba->auth_method == uaRADIUS)
+	{
+		if (hba->radiusserver)
+		{
+			hba_add_jsonb_string_key(&parseState, "radiusserver");
+			hba_add_jsonb_string_value(&parseState, hba->radiusserver);
+		}
+
+		if (hba->radiussecret)
+		{
+			hba_add_jsonb_string_key(&parseState, "radiussecret");
+			hba_add_jsonb_string_value(&parseState, hba->radiussecret);
+		}
+
+		if (hba->radiusidentifier)
+		{
+			hba_add_jsonb_string_key(&parseState, "radiusidentifier");
+			hba_add_jsonb_string_value(&parseState, hba->radiusidentifier);
+		}
+
+		if (hba->radiusport)
+		{
+			hba_add_jsonb_string_key(&parseState, "radiusport");
+			hba_add_jsonb_int32_value(&parseState, hba->radiusport);
+		}
+	}
+
+	result = pushJsonbValue(&parseState, WJB_END_OBJECT, NULL);
+	return JsonbValueToJsonb(result);
+}
+
+
+static void
+lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason)
+{
+	Datum			values[NUM_PG_HBA_LOOKUP_ATTS];
+	bool			nulls[NUM_PG_HBA_LOOKUP_ATTS];
+	ListCell	   *dbcell;
+	char			buffer[NI_MAXHOST];
+	HeapTuple		tuple;
+	int				index;
+	MemoryContext	old_cxt;
+	LookupHbalineContext   *mycxt;
+
+	mycxt = (LookupHbalineContext *) context;
+
+	index = 0;
+	memset(values, 0, sizeof(values));
+	memset(nulls, 0, sizeof(nulls));
+
+	old_cxt = MemoryContextSwitchTo(mycxt->memcxt);
+
+	/* line_number */
+	values[index] = Int32GetDatum(hba->linenumber);
+
+	/* mode */
+	index++;
+	if (reason == NULL)
+		values[index] = CStringGetTextDatum("matched");
+	else
+		values[index] = CStringGetTextDatum("skipped");
+
+	/* 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;
+		default:
+			elog(ERROR, "unexpected connection type in parsed HBA entry");
+			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);
+		}
+
+		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);
+		}
+
+		values[index] = PointerGetDatum(strlist_to_textarray(roles));
+	}
+	else
+		nulls[index] = true;
+
+	/* address */
+	index++;
+	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+	}
+	else
+		nulls[index] = true;
+
+	/* netmask */
+	index++;
+	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+	}
+	else
+		nulls[index] = true;
+
+	/* hostname */
+	index++;
+	if (hba->hostname)
+		values[index] = CStringGetTextDatum(hba->hostname);
+	else
+		nulls[index] = true;
+
+	/* method */
+	index++;
+	values[index] = getauthmethod(hba->auth_method);
+
+	/* options */
+	index++;
+	values[index] = PointerGetDatum(gethba_options(hba));
+
+	/* reason */
+	index++;
+	if (reason)
+		values[index] = CStringGetTextDatum(reason);
+	else
+		nulls[index] = true;
+
+	tuple = heap_form_tuple(mycxt->tupdesc, values, nulls);
+	tuplestore_puttuple(mycxt->tuple_store, tuple);
+
+	MemoryContextSwitchTo(old_cxt);
+	return;
+}
+
+/*
+ * SQL-accessible SRF to return all the settings from the pg_hba.conf
+ * file.
+ */
+Datum
+pg_hba_lookup(PG_FUNCTION_ARGS)
+{
+	hbaPort				   *port;
+	Tuplestorestate		   *tuple_store;
+	TupleDesc				tupdesc;
+	MemoryContext			old_cxt;
+	LookupHbalineContext   *mycxt;
+	ReturnSetInfo		   *rsi;
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to view pg_hba.conf settings"))));
+
+	/*
+	 * 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;
+
+	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+		(rsi->allowedModes & SFRM_Materialize) == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+
+	port = palloc0(sizeof(hbaPort));
+
+	if (PG_ARGISNULL(0))
+		ereport(ERROR,
+				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				 (errmsg("database name is required to match pg_hba configuration entry"))));
+	else
+		port->database_name = TextDatumGetCString(PG_GETARG_DATUM(0));
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				 (errmsg("user name is required to match pg_hba configuration entry"))));
+	else
+		port->user_name = TextDatumGetCString(PG_GETARG_DATUM(1));
+
+
+	if (!PG_ARGISNULL(2))
+	{
+		char	   *address = NULL;
+		struct addrinfo *gai_result = NULL;
+		struct addrinfo hints;
+		int			ret;
+
+		address = TextDatumGetCString(PG_GETARG_DATUM(2));
+
+		/* Get the IP address either way */
+		hints.ai_flags = AI_NUMERICHOST;
+		hints.ai_family = AF_UNSPEC;
+		hints.ai_socktype = 0;
+		hints.ai_protocol = 0;
+		hints.ai_addrlen = 0;
+		hints.ai_canonname = NULL;
+		hints.ai_addr = NULL;
+		hints.ai_next = NULL;
+
+		ret = getaddrinfo(address, NULL, &hints, &gai_result);
+		if (ret == 0 && gai_result)
+			memcpy(&port->raddr.addr, gai_result->ai_addr, gai_result->ai_addrlen);
+		else if (ret == EAI_NONAME)
+		{
+			struct addrinfo *gai_result2 = NULL;
+
+			port->remote_hostname = pstrdup(address);
+
+			ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result2);
+			if (ret == 0 && gai_result2)
+				memcpy(&port->raddr.addr, gai_result2->ai_addr, gai_result2->ai_addrlen);
+			else
+				ereport(ERROR,
+						(errmsg("getaddrinfo failed to look into hostname \"%s\": %s",
+								port->remote_hostname, gai_strerror(ret))));
+
+			if (gai_result2)
+				freeaddrinfo(gai_result2);
+		}
+		else
+			ereport(ERROR,
+					(errmsg("invalid IP address \"%s\": %s",
+							address, gai_strerror(ret))));
+		if (gai_result)
+			freeaddrinfo(gai_result);
+	}
+	else
+		port->raddr.addr.ss_family = AF_UNIX;
+
+	port->ssl_in_use = DatumGetBool(PG_GETARG_DATUM(3));
+
+	rsi->returnMode = SFRM_Materialize;
+
+	if (!load_hba())
+		ereport(ERROR,
+				(errmsg("Failed to load pg_hba.conf file"),
+				 errhint("More details may be available in the server log.")));
+
+	/*
+	 * Create the tupledesc and tuplestore in the per_query context as
+	 * required for SFRM_Materialize.
+	 */
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+
+	tupdesc = CreateTemplateTupleDesc(NUM_PG_HBA_LOOKUP_ATTS, false);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "line_number",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "mode",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "type",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "database",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "user_name",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "address",
+					   INETOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "netmask",
+					   INETOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "hostname",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "method",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "options",
+					   JSONBOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "reason",
+					   TEXTOID, -1, 0);
+	BlessTupleDesc(tupdesc);
+
+	tuple_store =
+		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+							  false, work_mem);
+
+	MemoryContextSwitchTo(old_cxt);
+
+	mycxt = (LookupHbalineContext *) palloc(sizeof(LookupHbalineContext));
+	mycxt->memcxt = AllocSetContextCreate(CurrentMemoryContext,
+										  "pg_hba_lookup tuple cxt",
+										  ALLOCSET_DEFAULT_MINSIZE,
+										  ALLOCSET_DEFAULT_INITSIZE,
+										  ALLOCSET_DEFAULT_MAXSIZE);
+	mycxt->tupdesc = tupdesc;
+	mycxt->tuple_store = tuple_store;
+
+	check_hba(port, lookup_hba_line_callback, mycxt);
+
+	MemoryContextDelete(mycxt->memcxt);
+	pfree(mycxt);
+
+	/* Clean up the memory allocated by load_hba() explicitly. */
+	discard_hba();
+
+	rsi->setDesc = tupdesc;
+	rsi->setResult = tuple_store;
+
+	PG_RETURN_NULL();
 }
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d8640db..ebd96da 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3079,6 +3079,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 = 3997 (  pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f f t v u 4 0 2249 "25 25 25 16" "{25,25,25,16,23,25,25,1009,1009,869,869,25,25,3802,25}" "{i,i,i,i,o,o,o,o,o,o,o,o,o,o,o}" "{database,user_name,address,ssl_inuse,line_number,mode,type,database,user_name,address,netmask,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup _null_ _null_ _null_));
+DESCR("view client authentication settings");
 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 = 1065 (  pg_prepared_xact PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{28,25,1184,26,26}" "{o,o,o,o,o}" "{transaction,gid,prepared,ownerid,dbid}" _null_ _null_ pg_prepared_xact _null_ _null_ _null_ ));
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index e610bf3..2d60879 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1123,6 +1123,9 @@ extern Datum set_config_by_name(PG_FUNCTION_ARGS);
 extern Datum show_all_settings(PG_FUNCTION_ARGS);
 extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
 
+/* hba.c */
+extern Datum pg_hba_lookup(PG_FUNCTION_ARGS);
+
 /* rls.c */
 extern Datum row_security_active(PG_FUNCTION_ARGS);
 extern Datum row_security_active_name(PG_FUNCTION_ARGS);
discard_hba_and_ident_cxt.patchapplication/octet-stream; name=discard_hba_and_ident_cxt.patchDownload
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 94f7cfa..58a4565 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -1770,8 +1770,7 @@ load_hba(void)
 	FreeFile(file);
 
 	/* Now parse all the lines */
-	Assert(PostmasterContext);
-	hbacxt = AllocSetContextCreate(PostmasterContext,
+	hbacxt = AllocSetContextCreate(CurrentMemoryContext,
 								   "hba parser context",
 								   ALLOCSET_DEFAULT_MINSIZE,
 								   ALLOCSET_DEFAULT_MINSIZE,
@@ -1829,14 +1828,26 @@ load_hba(void)
 	}
 
 	/* Loaded new file successfully, replace the one we use */
-	if (parsed_hba_context != NULL)
-		MemoryContextDelete(parsed_hba_context);
+	discard_hba();
 	parsed_hba_context = hbacxt;
 	parsed_hba_lines = new_parsed_lines;
 
 	return true;
 }
 
+
+void
+discard_hba(void)
+{
+	if (parsed_hba_context != NULL)
+	{
+		MemoryContextDelete(parsed_hba_context);
+		parsed_hba_context = NULL;
+		parsed_hba_lines = NIL;
+	}
+}
+
+
 /*
  * Parse one tokenised line from the ident config file and store the result in
  * an IdentLine structure, or NULL if parsing fails.
@@ -2148,8 +2159,7 @@ load_ident(void)
 	FreeFile(file);
 
 	/* Now parse all the lines */
-	Assert(PostmasterContext);
-	ident_context = AllocSetContextCreate(PostmasterContext,
+	ident_context = AllocSetContextCreate(CurrentMemoryContext,
 										  "ident parser context",
 										  ALLOCSET_DEFAULT_MINSIZE,
 										  ALLOCSET_DEFAULT_MINSIZE,
@@ -2202,6 +2212,20 @@ load_ident(void)
 	}
 
 	/* Loaded new file successfully, replace the one we use */
+	discard_ident();
+	parsed_ident_context = ident_context;
+	parsed_ident_lines = new_parsed_lines;
+
+	return true;
+}
+
+
+void
+discard_ident(void)
+{
+	ListCell   *parsed_line_cell;
+	IdentLine  *newline;
+
 	if (parsed_ident_lines != NIL)
 	{
 		foreach(parsed_line_cell, parsed_ident_lines)
@@ -2210,18 +2234,17 @@ load_ident(void)
 			if (newline->ident_user[0] == '/')
 				pg_regfree(&newline->re);
 		}
+		parsed_ident_lines = NIL;
 	}
+
 	if (parsed_ident_context != NULL)
+	{
 		MemoryContextDelete(parsed_ident_context);
-
-	parsed_ident_context = ident_context;
-	parsed_ident_lines = new_parsed_lines;
-
-	return true;
+		parsed_ident_context = NULL;
+	}
 }
 
 
-
 /*
  *	Determine what authentication method should be used when accessing database
  *	"database" from frontend "raddr", user "user".  Return the method and
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 7b19714..073b432 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -191,18 +191,6 @@ PerformAuthentication(Port *port)
 	 * FIXME: [fork/exec] Ugh.  Is there a way around this overhead?
 	 */
 #ifdef EXEC_BACKEND
-	/*
-	 * load_hba() and load_ident() want to work within the PostmasterContext,
-	 * so create that if it doesn't exist (which it won't).  We'll delete it
-	 * again later, in PostgresMain.
-	 */
-	if (PostmasterContext == NULL)
-		PostmasterContext = AllocSetContextCreate(TopMemoryContext,
-												  "Postmaster",
-												  ALLOCSET_DEFAULT_MINSIZE,
-												  ALLOCSET_DEFAULT_INITSIZE,
-												  ALLOCSET_DEFAULT_MAXSIZE);
-
 	if (!load_hba())
 	{
 		/*
@@ -736,6 +724,14 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 	}
 
 	/*
+	 * We don't need the HBA and ident data going forward, but we can't rely
+	 * on release of PostmasterContext to clean that up, so discard them
+	 * explicitly here.
+	 */
+	discard_hba();
+	discard_ident();
+
+	/*
 	 * If we're trying to shut down, only superusers can connect, and new
 	 * replication connections are not allowed.
 	 */
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index 68a953a..291300b 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -98,6 +98,10 @@ typedef struct Port hbaPort;
 
 extern bool load_hba(void);
 extern bool load_ident(void);
+
+extern void discard_hba(void);
+extern void discard_ident(void);
+
 extern void hba_getauthmethod(hbaPort *port);
 extern int check_usermap(const char *usermap_name,
 			  const char *pg_role, const char *auth_user,
#48Shulgin, Oleksandr
oleksandr.shulgin@zalando.de
In reply to: Haribabu Kommi (#47)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Wed, Dec 30, 2015 at 4:31 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

Adding quotes to pg_hba_lookup function makes it different from others.
The issues regarding the same is already discussed in [1].

select a.database[1], b.datname from
pg_hba_lookup('postgres','kommih','::1')
as a, pg_database as b where a.database[1]
= b.datname;

The queries like above are not possible with quoted output. It is very
rare that the
pg_hba_lookup function used in join operations, but still it is better
to provide
data without quotes. so I reverted these changes in the attached latest
patch.

That's a good point. I wonder that maybe instead of re-introducing quotes
we could somehow make the unquoted keywords that have special meaning stand
out, e.g:

database | {$sameuser}
user_name | {$all}

That should make it obvious which of the values are placeholders and
doesn't interfere with joining database or user catalogs (while I would
call "sameuser" a very unlikely name for a database, "all" might be not
that unlikely name for a user, e.g. someone called like "Albert L. Lucky"
could use that as a login name).

--
Alex

#49Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Shulgin, Oleksandr (#48)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Wed, Dec 30, 2015 at 9:48 PM, Shulgin, Oleksandr
<oleksandr.shulgin@zalando.de> wrote:

On Wed, Dec 30, 2015 at 4:31 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

Adding quotes to pg_hba_lookup function makes it different from others.
The issues regarding the same is already discussed in [1].

select a.database[1], b.datname from
pg_hba_lookup('postgres','kommih','::1')
as a, pg_database as b where a.database[1]
= b.datname;

The queries like above are not possible with quoted output. It is very
rare that the
pg_hba_lookup function used in join operations, but still it is better
to provide
data without quotes. so I reverted these changes in the attached latest
patch.

That's a good point. I wonder that maybe instead of re-introducing quotes
we could somehow make the unquoted keywords that have special meaning stand
out, e.g:

database | {$sameuser}
user_name | {$all}

That should make it obvious which of the values are placeholders and doesn't
interfere with joining database or user catalogs (while I would call
"sameuser" a very unlikely name for a database, "all" might be not that
unlikely name for a user, e.g. someone called like "Albert L. Lucky" could
use that as a login name).

It is not only the problem with joins, the following two cases works
without quotes only.
With quotes the query doesn't match with the database name.

select * from pg_hba_lookup('Test', 'kommih','127.0.0.1') where
database = '{"Test"}';
select * from pg_hba_lookup('Test', 'kommih','127.0.0.1') where
database = '{Test}';

Regards,
Hari Babu
Fujitsu Australia

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#50Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Haribabu Kommi (#49)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Thu, Dec 31, 2015 at 10:47 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

On Wed, Dec 30, 2015 at 9:48 PM, Shulgin, Oleksandr
<oleksandr.shulgin@zalando.de> wrote:

On Wed, Dec 30, 2015 at 4:31 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

Adding quotes to pg_hba_lookup function makes it different from others.
The issues regarding the same is already discussed in [1].

select a.database[1], b.datname from
pg_hba_lookup('postgres','kommih','::1')
as a, pg_database as b where a.database[1]
= b.datname;

The queries like above are not possible with quoted output. It is very
rare that the
pg_hba_lookup function used in join operations, but still it is better
to provide
data without quotes. so I reverted these changes in the attached latest
patch.

That's a good point. I wonder that maybe instead of re-introducing quotes
we could somehow make the unquoted keywords that have special meaning stand
out, e.g:

database | {$sameuser}
user_name | {$all}

That should make it obvious which of the values are placeholders and doesn't
interfere with joining database or user catalogs (while I would call
"sameuser" a very unlikely name for a database, "all" might be not that
unlikely name for a user, e.g. someone called like "Albert L. Lucky" could
use that as a login name).

It is not only the problem with joins, the following two cases works
without quotes only.
With quotes the query doesn't match with the database name.

select * from pg_hba_lookup('Test', 'kommih','127.0.0.1') where
database = '{"Test"}';
select * from pg_hba_lookup('Test', 'kommih','127.0.0.1') where
database = '{Test}';

Hi, Do you have any further comments on the patch that needs to be
taken care?

Regards,
Hari Babu
Fujitsu Australia

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#51Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Haribabu Kommi (#50)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

Haribabu Kommi wrote:

Hi, Do you have any further comments on the patch that needs to be
taken care?

I do. I think the jsonb functions you added should be added to one of
the json .c files instead, since they seem of general applicability.
But actually, I don't think you have ever replied to the question of why
are you using JSON in the first place; isn't a normal array suitable?

The callback stuff is not documented in check_hba() at all. Can you
please add an explanation just above the function, so that people trying
to use it know what can the callback be used for? Also a few lines
above the callback itself would be good. Also, the third argument of
check_hba() is a translatable message so you should enclose it in _() so
that it is picked up for translation. The "skipped"/"matched" words
(and maybe others?) need to be marked similarly.

That "Failed" in the errmsg in pg_hba_lookup() should be lowercase.

Moving to next CF.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#52Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Alvaro Herrera (#51)
1 attachment(s)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Tue, Feb 2, 2016 at 8:57 AM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:

Haribabu Kommi wrote:

Hi, Do you have any further comments on the patch that needs to be
taken care?

I do. I think the jsonb functions you added should be added to one of
the json .c files instead, since they seem of general applicability.

moved these functions into jsonb_util.c file.

But actually, I don't think you have ever replied to the question of why
are you using JSON in the first place; isn't a normal array suitable?

It was discussed and told to use JSON for options instead of array in [1]/messages/by-id/5547DB0A.2020904@gmx.net,
because of that reason I changed.

The callback stuff is not documented in check_hba() at all. Can you
please add an explanation just above the function, so that people trying
to use it know what can the callback be used for? Also a few lines
above the callback itself would be good.

Added some details in explaining the call back function.

Also, the third argument of
check_hba() is a translatable message so you should enclose it in _() so
that it is picked up for translation. The "skipped"/"matched" words
(and maybe others?) need to be marked similarly.

Changed mode column (skipped/matched) and reason for mismatch details
are enclosed in _() for translation. Do you find any other details needs to be
enclosed?

That "Failed" in the errmsg in pg_hba_lookup() should be lowercase.

corrected.

Moving to next CF.

Thanks. updated patch is attached with the above corrections.
This patch needs to be applied on top discard_hba_and_ident_cxt patch
that is posted earlier.

[1]: /messages/by-id/5547DB0A.2020904@gmx.net

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_hba_lookup_poc_v12.patchapplication/octet-stream; name=pg_hba_lookup_poc_v12.patchDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 139aa2b..977e5a0 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -16679,6 +16679,19 @@ SELECT collation for ('foo' COLLATE "de_DE");
        <entry><type>text</type></entry>
        <entry>set parameter and return new value</entry>
       </row>
+      <row>
+       <entry>
+        <indexterm>
+         <primary>pg_hba_lookup</primary>
+        </indexterm>
+        <literal><function>pg_hba_lookup(<parameter>database</> <type>text</>,
+                            <parameter>user_name</> <type>text</>
+                            [, <parameter>address</> <type>text</>]
+                            [, <parameter>ssl_inuse</> <type>text</>)</function></literal>
+       </entry>
+       <entry><type>record</type></entry>
+       <entry>scan <filename>pg_hba.conf</> and return examined entries up to a matching one</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
@@ -16736,6 +16749,32 @@ SELECT set_config('log_statement_stats', 'off', false);
 </programlisting>
    </para>
 
+   <para>
+    <function>pg_hba_lookup</function> returns a set of records containing the
+    line number, mode, type, database, user_name, address, netmask, hostname,
+    method, options and skip reason. For example, to debug problems with user
+    <literal>kommih</> trying to connect to a database <literal>postgres</>
+    from IPv6-address <literal>::1</>, one can issue a following query:
+<programlisting>
+postgres=# SELECT * FROM pg_hba_lookup('postgres', 'kommih', '::1');
+ line_number |  mode   | type  | database | user_name |  address  |                 netmask                 | hostname | method | options |          reason          
+-------------+---------+-------+----------+-----------+-----------+-----------------------------------------+----------+--------+---------+--------------------------
+          84 | skipped | local | {all}    | {all}     |           |                                         |          | trust  | {}      | connection type mismatch
+          86 | skipped | host  | {all}    | {all}     | 127.0.0.1 | 255.255.255.255                         |          | trust  | {}      | IP address mismatch
+          88 | matched | host  | {all}    | {all}     | ::1       | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff |          | trust  | {}      | 
+(3 rows)
+
+</programlisting>
+    This function actually loads the contents of <filename>pg_hba.conf</> file
+    into memory to perform matching, thus a database administrator can use it
+    to test the effects of changes made to the file in isolation prior to
+    actually applying the configuration cluster-wide with a signal to the
+    postmaster process.
+
+    Only superusers can access this function to look the
+    <filename>pg_hba.conf</> entries up.
+   </para>
+   
   </sect2>
 
   <sect2 id="functions-admin-signal">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 923fe58..2943d41 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -965,3 +965,12 @@ RETURNS jsonb
 LANGUAGE INTERNAL
 STRICT IMMUTABLE
 AS 'jsonb_set';
+
+CREATE OR REPLACE FUNCTION pg_hba_lookup(IN database text, IN user_name text,
+    IN address text DEFAULT NULL, IN ssl_inuse boolean DEFAULT false,
+	OUT line_number int, OUT mode text, OUT type text, OUT database text[],
+	OUT user_name text[], OUT address inet, OUT netmask inet,
+        OUT hostname text, OUT method text, OUT options jsonb, OUT reason text)
+RETURNS SETOF RECORD
+LANGUAGE INTERNAL
+AS 'pg_hba_lookup';
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 9f94e63..d0099dc 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -25,15 +25,22 @@
 #include <arpa/inet.h>
 #include <unistd.h>
 
+#include "access/htup_details.h"
+#include "catalog/objectaddress.h"
 #include "catalog/pg_collation.h"
+#include "catalog/pg_type.h"
+#include "funcapi.h"
 #include "libpq/ip.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/jsonb.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 
@@ -52,6 +59,8 @@
 #define MAX_TOKEN	256
 #define MAX_LINE	8192
 
+#define NUM_PG_HBA_LOOKUP_ATTS	 11
+
 /* callback data for check_network_callback */
 typedef struct check_network_data
 {
@@ -75,6 +84,24 @@ typedef struct HbaToken
 } HbaToken;
 
 /*
+ * Optional callback function type for check_hba() function.
+ * Currently, valid callback function is passed to check_hba()
+ * by pg_hba_lookup system function to frame the hba tuple row
+ * based on inputs provided by the user.
+ */
+typedef void (*hba_line_callback) (void *context, HbaLine *hba_line,
+											   const char *reason);
+
+/* Context to use with hba_line_callback function. */
+typedef struct
+{
+	MemoryContext memcxt;
+	TupleDesc	tupdesc;
+	Tuplestorestate *tuple_store;
+}	HbalineContext;
+
+
+/*
  * pre-parsed content of HBA config file: list of HbaLine structs.
  * parsed_hba_context is the memory context where it lives.
  */
@@ -99,6 +126,9 @@ 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);
+static Datum getauthmethod(UserAuth auth_method);
+static Jsonb *gethba_options(HbaLine *hba);
+static void lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason);
 
 /*
  * isblank() exists in the ISO C99 spec, but it's not very portable yet,
@@ -1640,7 +1670,8 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  *	request.
  */
 static void
-check_hba(hbaPort *port)
+check_hba(hbaPort *port, hba_line_callback callback,
+		  void *callback_context)
 {
 	Oid			roleid;
 	ListCell   *line;
@@ -1657,25 +1688,41 @@ check_hba(hbaPort *port)
 		if (hba->conntype == ctLocal)
 		{
 			if (!IS_AF_UNIX(port->raddr.addr.ss_family))
+			{
+				if (callback)
+					callback(callback_context, hba, _("connection type mismatch"));
 				continue;
+			}
 		}
 		else
 		{
 			if (IS_AF_UNIX(port->raddr.addr.ss_family))
+			{
+				if (callback)
+					callback(callback_context, hba, _("connection type mismatch"));
 				continue;
+			}
 
 			/* Check SSL state */
 			if (port->ssl_in_use)
 			{
 				/* Connection is SSL, match both "host" and "hostssl" */
 				if (hba->conntype == ctHostNoSSL)
+				{
+					if (callback)
+						callback(callback_context, hba, _("connection type mismatch"));
 					continue;
+				}
 			}
 			else
 			{
 				/* Connection is not SSL, match both "host" and "hostnossl" */
 				if (hba->conntype == ctHostSSL)
+				{
+					if (callback)
+						callback(callback_context, hba, _("connection type mismatch"));
 					continue;
+				}
 			}
 
 			/* Check IP address */
@@ -1686,14 +1733,22 @@ check_hba(hbaPort *port)
 					{
 						if (!check_hostname(port,
 											hba->hostname))
+						{
+							if (callback)
+								callback(callback_context, hba, _("hostname mismatch"));
 							continue;
+						}
 					}
 					else
 					{
 						if (!check_ip(&port->raddr,
 									  (struct sockaddr *) & hba->addr,
 									  (struct sockaddr *) & hba->mask))
+						{
+							if (callback)
+								callback(callback_context, hba, _("IP address mismatch"));
 							continue;
+						}
 					}
 					break;
 				case ipCmpAll:
@@ -1702,7 +1757,11 @@ check_hba(hbaPort *port)
 				case ipCmpSameNet:
 					if (!check_same_host_or_net(&port->raddr,
 												hba->ip_cmp_method))
+					{
+						if (callback)
+							callback(callback_context, hba, _("samehost/samenet mismatch"));
 						continue;
+					}
 					break;
 				default:
 					/* shouldn't get here, but deem it no-match if so */
@@ -1713,10 +1772,21 @@ check_hba(hbaPort *port)
 		/* Check database and role */
 		if (!check_db(port->database_name, port->user_name, roleid,
 					  hba->databases))
+		{
+			if (callback)
+				callback(callback_context, hba, _("database name mismatch"));
 			continue;
+		}
 
 		if (!check_role(port->user_name, roleid, hba->roles))
+		{
+			if (callback)
+				callback(callback_context, hba, _("user name mismatch"));
 			continue;
+		}
+
+		if (callback)
+			callback(callback_context, hba, NULL);
 
 		/* Found a record that matched! */
 		port->hba = hba;
@@ -2256,5 +2326,505 @@ discard_ident(void)
 void
 hba_getauthmethod(hbaPort *port)
 {
-	check_hba(port);
+	check_hba(port, NULL, NULL);
+}
+
+
+/*
+ * 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 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 uaLDAP:
+			result = CStringGetTextDatum("ldap");
+			break;
+		case uaCert:
+			result = CStringGetTextDatum("cert");
+			break;
+		case uaRADIUS:
+			result = CStringGetTextDatum("radius");
+			break;
+		case uaPeer:
+			result = CStringGetTextDatum("peer");
+			break;
+		default:
+			elog(ERROR, "unexpected authentication method in parsed HBA entry");
+			break;
+	}
+
+	return result;
+}
+
+static Jsonb *
+gethba_options(HbaLine *hba)
+{
+	JsonbParseState *parseState = NULL;
+	JsonbValue *result;
+
+	result = pushJsonbValue(&parseState, WJB_BEGIN_OBJECT, NULL);
+
+	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
+	{
+		if (hba->include_realm)
+		{
+			push_jsonb_string_key(&parseState, "include_realm");
+			push_jsonb_bool_value(&parseState, true);
+		}
+
+		if (hba->krb_realm)
+		{
+			push_jsonb_string_key(&parseState, "krb_realm");
+			push_jsonb_string_value(&parseState, hba->krb_realm);
+		}
+	}
+
+	if (hba->usermap)
+	{
+		push_jsonb_string_key(&parseState, "map");
+		push_jsonb_string_value(&parseState, hba->usermap);
+	}
+
+	if (hba->clientcert)
+	{
+		push_jsonb_string_key(&parseState, "clientcert");
+		push_jsonb_bool_value(&parseState, true);
+	}
+
+	if (hba->pamservice)
+	{
+		push_jsonb_string_key(&parseState, "pamservice");
+		push_jsonb_string_value(&parseState, hba->pamservice);
+	}
+
+	if (hba->auth_method == uaLDAP)
+	{
+		if (hba->ldapserver)
+		{
+			push_jsonb_string_key(&parseState, "ldapserver");
+			push_jsonb_string_value(&parseState, hba->ldapserver);
+		}
+
+		if (hba->ldapport)
+		{
+			push_jsonb_string_key(&parseState, "ldapport");
+			push_jsonb_int32_value(&parseState, hba->ldapport);
+		}
+
+		if (hba->ldaptls)
+		{
+			push_jsonb_string_key(&parseState, "ldaptls");
+			push_jsonb_bool_value(&parseState, true);
+		}
+
+		if (hba->ldapprefix)
+		{
+			push_jsonb_string_key(&parseState, "ldapprefix");
+			push_jsonb_string_value(&parseState, hba->ldapprefix);
+		}
+
+		if (hba->ldapsuffix)
+		{
+			push_jsonb_string_key(&parseState, "ldapsuffix");
+			push_jsonb_string_value(&parseState, hba->ldapsuffix);
+		}
+
+		if (hba->ldapbasedn)
+		{
+			push_jsonb_string_key(&parseState, "ldapbasedn");
+			push_jsonb_string_value(&parseState, hba->ldapbasedn);
+		}
+
+		if (hba->ldapbinddn)
+		{
+			push_jsonb_string_key(&parseState, "ldapbinddn");
+			push_jsonb_string_value(&parseState, hba->ldapbinddn);
+		}
+
+		if (hba->ldapbindpasswd)
+		{
+			push_jsonb_string_key(&parseState, "ldapbindpasswd");
+			push_jsonb_string_value(&parseState, hba->ldapbindpasswd);
+		}
+
+		if (hba->ldapsearchattribute)
+		{
+			push_jsonb_string_key(&parseState, "ldapsearchattribute");
+			push_jsonb_string_value(&parseState, hba->ldapsearchattribute);
+		}
+
+		if (hba->ldapscope)
+		{
+			push_jsonb_string_key(&parseState, "ldapscope");
+			push_jsonb_int32_value(&parseState, hba->ldapscope);
+		}
+	}
+
+	if (hba->auth_method == uaRADIUS)
+	{
+		if (hba->radiusserver)
+		{
+			push_jsonb_string_key(&parseState, "radiusserver");
+			push_jsonb_string_value(&parseState, hba->radiusserver);
+		}
+
+		if (hba->radiussecret)
+		{
+			push_jsonb_string_key(&parseState, "radiussecret");
+			push_jsonb_string_value(&parseState, hba->radiussecret);
+		}
+
+		if (hba->radiusidentifier)
+		{
+			push_jsonb_string_key(&parseState, "radiusidentifier");
+			push_jsonb_string_value(&parseState, hba->radiusidentifier);
+		}
+
+		if (hba->radiusport)
+		{
+			push_jsonb_string_key(&parseState, "radiusport");
+			push_jsonb_int32_value(&parseState, hba->radiusport);
+		}
+	}
+
+	result = pushJsonbValue(&parseState, WJB_END_OBJECT, NULL);
+	return JsonbValueToJsonb(result);
+}
+
+
+static void
+lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason)
+{
+	Datum		values[NUM_PG_HBA_LOOKUP_ATTS];
+	bool		nulls[NUM_PG_HBA_LOOKUP_ATTS];
+	ListCell   *dbcell;
+	char		buffer[NI_MAXHOST];
+	HeapTuple	tuple;
+	int			index;
+	MemoryContext old_cxt;
+	HbalineContext *mycxt;
+
+	mycxt = (HbalineContext *) context;
+
+	index = 0;
+	memset(values, 0, sizeof(values));
+	memset(nulls, 0, sizeof(nulls));
+
+	old_cxt = MemoryContextSwitchTo(mycxt->memcxt);
+
+	/* line_number */
+	values[index] = Int32GetDatum(hba->linenumber);
+
+	/* mode */
+	index++;
+	if (reason == NULL)
+		values[index] = CStringGetTextDatum(_("matched"));
+	else
+		values[index] = CStringGetTextDatum(_("skipped"));
+
+	/* 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;
+		default:
+			elog(ERROR, "unexpected connection type in parsed HBA entry");
+			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);
+		}
+
+		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);
+		}
+
+		values[index] = PointerGetDatum(strlist_to_textarray(roles));
+	}
+	else
+		nulls[index] = true;
+
+	/* address */
+	index++;
+	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+	}
+	else
+		nulls[index] = true;
+
+	/* netmask */
+	index++;
+	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+	}
+	else
+		nulls[index] = true;
+
+	/* hostname */
+	index++;
+	if (hba->hostname)
+		values[index] = CStringGetTextDatum(hba->hostname);
+	else
+		nulls[index] = true;
+
+	/* method */
+	index++;
+	values[index] = getauthmethod(hba->auth_method);
+
+	/* options */
+	index++;
+	values[index] = PointerGetDatum(gethba_options(hba));
+
+	/* reason */
+	index++;
+	if (reason)
+		values[index] = CStringGetTextDatum(reason);
+	else
+		nulls[index] = true;
+
+	tuple = heap_form_tuple(mycxt->tupdesc, values, nulls);
+	tuplestore_puttuple(mycxt->tuple_store, tuple);
+
+	MemoryContextSwitchTo(old_cxt);
+	return;
+}
+
+/*
+ * SQL-accessible SRF to return all the settings from the pg_hba.conf
+ * file.
+ */
+Datum
+pg_hba_lookup(PG_FUNCTION_ARGS)
+{
+	hbaPort    *port;
+	Tuplestorestate *tuple_store;
+	TupleDesc	tupdesc;
+	MemoryContext old_cxt;
+	HbalineContext *mycxt;
+	ReturnSetInfo *rsi;
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to view pg_hba.conf settings"))));
+
+	/*
+	 * 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;
+
+	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+		(rsi->allowedModes & SFRM_Materialize) == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+
+	port = palloc0(sizeof(hbaPort));
+
+	if (PG_ARGISNULL(0))
+		ereport(ERROR,
+				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				 (errmsg("database name is required to match pg_hba configuration entry"))));
+	else
+		port->database_name = TextDatumGetCString(PG_GETARG_DATUM(0));
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				 (errmsg("user name is required to match pg_hba configuration entry"))));
+	else
+		port->user_name = TextDatumGetCString(PG_GETARG_DATUM(1));
+
+
+	if (!PG_ARGISNULL(2))
+	{
+		char	   *address = NULL;
+		struct addrinfo *gai_result = NULL;
+		struct addrinfo hints;
+		int			ret;
+
+		address = TextDatumGetCString(PG_GETARG_DATUM(2));
+
+		/* Get the IP address either way */
+		hints.ai_flags = AI_NUMERICHOST;
+		hints.ai_family = AF_UNSPEC;
+		hints.ai_socktype = 0;
+		hints.ai_protocol = 0;
+		hints.ai_addrlen = 0;
+		hints.ai_canonname = NULL;
+		hints.ai_addr = NULL;
+		hints.ai_next = NULL;
+
+		ret = getaddrinfo(address, NULL, &hints, &gai_result);
+		if (ret == 0 && gai_result)
+			memcpy(&port->raddr.addr, gai_result->ai_addr, gai_result->ai_addrlen);
+		else if (ret == EAI_NONAME)
+		{
+			struct addrinfo *gai_result2 = NULL;
+
+			port->remote_hostname = pstrdup(address);
+
+			ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result2);
+			if (ret == 0 && gai_result2)
+				memcpy(&port->raddr.addr, gai_result2->ai_addr, gai_result2->ai_addrlen);
+			else
+				ereport(ERROR,
+				(errmsg("getaddrinfo failed to look into hostname \"%s\": %s",
+						port->remote_hostname, gai_strerror(ret))));
+
+			if (gai_result2)
+				freeaddrinfo(gai_result2);
+		}
+		else
+			ereport(ERROR,
+					(errmsg("invalid IP address \"%s\": %s",
+							address, gai_strerror(ret))));
+		if (gai_result)
+			freeaddrinfo(gai_result);
+	}
+	else
+		port->raddr.addr.ss_family = AF_UNIX;
+
+	port->ssl_in_use = DatumGetBool(PG_GETARG_DATUM(3));
+
+	rsi->returnMode = SFRM_Materialize;
+
+	if (!load_hba())
+		ereport(ERROR,
+				(errmsg("failed to load pg_hba.conf file"),
+			   errhint("more details may be available in the server log.")));
+
+	/*
+	 * Create the tupledesc and tuplestore in the per_query context as
+	 * required for SFRM_Materialize.
+	 */
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+
+	tupdesc = CreateTemplateTupleDesc(NUM_PG_HBA_LOOKUP_ATTS, false);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "line_number",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "mode",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "type",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "database",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "user_name",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "address",
+					   INETOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "netmask",
+					   INETOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "hostname",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "method",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "options",
+					   JSONBOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "reason",
+					   TEXTOID, -1, 0);
+	BlessTupleDesc(tupdesc);
+
+	tuple_store =
+		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+							  false, work_mem);
+
+	MemoryContextSwitchTo(old_cxt);
+
+	mycxt = (HbalineContext *) palloc(sizeof(HbalineContext));
+	mycxt->memcxt = AllocSetContextCreate(CurrentMemoryContext,
+										  "pg_hba_lookup tuple cxt",
+										  ALLOCSET_DEFAULT_MINSIZE,
+										  ALLOCSET_DEFAULT_INITSIZE,
+										  ALLOCSET_DEFAULT_MAXSIZE);
+	mycxt->tupdesc = tupdesc;
+	mycxt->tuple_store = tuple_store;
+
+	check_hba(port, lookup_hba_line_callback, mycxt);
+
+	MemoryContextDelete(mycxt->memcxt);
+	pfree(mycxt);
+
+	/* Clean up the memory allocated by load_hba() explicitly. */
+	discard_hba();
+
+	rsi->setDesc = tupdesc;
+	rsi->setResult = tuple_store;
+
+	PG_RETURN_NULL();
 }
diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c
index e1ceea6..171e532 100644
--- a/src/backend/utils/adt/jsonb_util.c
+++ b/src/backend/utils/adt/jsonb_util.c
@@ -1802,3 +1802,49 @@ uniqueifyJsonbObject(JsonbValue *object)
 		object->val.object.nPairs = res + 1 - object->val.object.pairs;
 	}
 }
+
+void
+push_jsonb_string_key(JsonbParseState **pstate, char *string_key)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvString;
+	jb.val.string.len = strlen(string_key);
+	jb.val.string.val = pstrdup(string_key);
+	pushJsonbValue(pstate, WJB_KEY, &jb);
+}
+
+void
+push_jsonb_bool_value(JsonbParseState **pstate, bool bool_val)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvBool;
+	jb.val.boolean = bool_val;
+
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
+
+void
+push_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val)
+{
+	JsonbValue	jb;
+	char		outputstr[64];
+
+	snprintf(outputstr, 64, "%d", int32_val);
+	jb.type = jbvNumeric;
+	jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1));
+
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
+
+void
+push_jsonb_string_value(JsonbParseState **pstate, char *string_value)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvString;
+	jb.val.string.len = strlen(string_value);
+	jb.val.string.val = pstrdup(string_value);
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 5ded13e..1ecd90b 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3004,6 +3004,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 = 3997 (  pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f f t v u 4 0 2249 "25 25 25 16" "{25,25,25,16,23,25,25,1009,1009,869,869,25,25,3802,25}" "{i,i,i,i,o,o,o,o,o,o,o,o,o,o,o}" "{database,user_name,address,ssl_inuse,line_number,mode,type,database,user_name,address,netmask,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup _null_ _null_ _null_));
+DESCR("view client authentication settings");
 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 = 1065 (  pg_prepared_xact PGNSP PGUID 12 1 1000 0 0 f f f f t t v s 0 0 2249 "" "{28,25,1184,26,26}" "{o,o,o,o,o}" "{transaction,gid,prepared,ownerid,dbid}" _null_ _null_ pg_prepared_xact _null_ _null_ _null_ ));
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 5e8e832..49fff86 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1143,6 +1143,9 @@ extern Datum set_config_by_name(PG_FUNCTION_ARGS);
 extern Datum show_all_settings(PG_FUNCTION_ARGS);
 extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
 
+/* hba.c */
+extern Datum pg_hba_lookup(PG_FUNCTION_ARGS);
+
 /* rls.c */
 extern Datum row_security_active(PG_FUNCTION_ARGS);
 extern Datum row_security_active_name(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 5f49d8d..33e00d2 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -427,6 +427,12 @@ extern bool JsonbDeepContains(JsonbIterator **val,
 				  JsonbIterator **mContained);
 extern void JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash);
 
+/* jsonb_util.c support functions */
+void push_jsonb_string_key(JsonbParseState **pstate, char *string_key);
+void push_jsonb_bool_value(JsonbParseState **pstate, bool bool_val);
+void push_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val);
+void push_jsonb_string_value(JsonbParseState **pstate, char *string_value);
+
 /* jsonb.c support functions */
 extern char *JsonbToCString(StringInfo out, JsonbContainer *in,
 			   int estimated_len);
#53Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Haribabu Kommi (#52)
1 attachment(s)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Fri, Feb 5, 2016 at 2:29 PM, Haribabu Kommi <kommi.haribabu@gmail.com> wrote:

This patch needs to be applied on top discard_hba_and_ident_cxt patch
that is posted earlier.

Here I attached a re-based patch to the latest head with inclusion of
discard_hba_ident_cxt patch for easier review as a single patch.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg_hba_lookup_poc_v13.patchapplication/octet-stream; name=pg_hba_lookup_poc_v13.patchDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index c0b94bc..4583400 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -16751,6 +16751,19 @@ SELECT collation for ('foo' COLLATE "de_DE");
        <entry><type>text</type></entry>
        <entry>set parameter and return new value</entry>
       </row>
+      <row>
+       <entry>
+        <indexterm>
+         <primary>pg_hba_lookup</primary>
+        </indexterm>
+        <literal><function>pg_hba_lookup(<parameter>database</> <type>text</>,
+                            <parameter>user_name</> <type>text</>
+                            [, <parameter>address</> <type>text</>]
+                            [, <parameter>ssl_inuse</> <type>text</>)</function></literal>
+       </entry>
+       <entry><type>record</type></entry>
+       <entry>scan <filename>pg_hba.conf</> and return examined entries up to a matching one</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
@@ -16808,6 +16821,32 @@ SELECT set_config('log_statement_stats', 'off', false);
 </programlisting>
    </para>
 
+   <para>
+    <function>pg_hba_lookup</function> returns a set of records containing the
+    line number, mode, type, database, user_name, address, netmask, hostname,
+    method, options and skip reason. For example, to debug problems with user
+    <literal>kommih</> trying to connect to a database <literal>postgres</>
+    from IPv6-address <literal>::1</>, one can issue a following query:
+<programlisting>
+postgres=# SELECT * FROM pg_hba_lookup('postgres', 'kommih', '::1');
+ line_number |  mode   | type  | database | user_name |  address  |                 netmask                 | hostname | method | options |          reason          
+-------------+---------+-------+----------+-----------+-----------+-----------------------------------------+----------+--------+---------+--------------------------
+          84 | skipped | local | {all}    | {all}     |           |                                         |          | trust  | {}      | connection type mismatch
+          86 | skipped | host  | {all}    | {all}     | 127.0.0.1 | 255.255.255.255                         |          | trust  | {}      | IP address mismatch
+          88 | matched | host  | {all}    | {all}     | ::1       | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff |          | trust  | {}      | 
+(3 rows)
+
+</programlisting>
+    This function actually loads the contents of <filename>pg_hba.conf</> file
+    into memory to perform matching, thus a database administrator can use it
+    to test the effects of changes made to the file in isolation prior to
+    actually applying the configuration cluster-wide with a signal to the
+    postmaster process.
+
+    Only superusers can access this function to look the
+    <filename>pg_hba.conf</> entries up.
+   </para>
+   
   </sect2>
 
   <sect2 id="functions-admin-signal">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index abf9a70..615a4d5 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -971,3 +971,12 @@ RETURNS jsonb
 LANGUAGE INTERNAL
 STRICT IMMUTABLE
 AS 'jsonb_set';
+
+CREATE OR REPLACE FUNCTION pg_hba_lookup(IN database text, IN user_name text,
+    IN address text DEFAULT NULL, IN ssl_inuse boolean DEFAULT false,
+	OUT line_number int, OUT mode text, OUT type text, OUT database text[],
+	OUT user_name text[], OUT address inet, OUT netmask inet,
+        OUT hostname text, OUT method text, OUT options jsonb, OUT reason text)
+RETURNS SETOF RECORD
+LANGUAGE INTERNAL
+AS 'pg_hba_lookup';
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 28f9fb5..d0099dc 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -25,15 +25,22 @@
 #include <arpa/inet.h>
 #include <unistd.h>
 
+#include "access/htup_details.h"
+#include "catalog/objectaddress.h"
 #include "catalog/pg_collation.h"
+#include "catalog/pg_type.h"
+#include "funcapi.h"
 #include "libpq/ip.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/jsonb.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 
@@ -52,6 +59,8 @@
 #define MAX_TOKEN	256
 #define MAX_LINE	8192
 
+#define NUM_PG_HBA_LOOKUP_ATTS	 11
+
 /* callback data for check_network_callback */
 typedef struct check_network_data
 {
@@ -75,6 +84,24 @@ typedef struct HbaToken
 } HbaToken;
 
 /*
+ * Optional callback function type for check_hba() function.
+ * Currently, valid callback function is passed to check_hba()
+ * by pg_hba_lookup system function to frame the hba tuple row
+ * based on inputs provided by the user.
+ */
+typedef void (*hba_line_callback) (void *context, HbaLine *hba_line,
+											   const char *reason);
+
+/* Context to use with hba_line_callback function. */
+typedef struct
+{
+	MemoryContext memcxt;
+	TupleDesc	tupdesc;
+	Tuplestorestate *tuple_store;
+}	HbalineContext;
+
+
+/*
  * pre-parsed content of HBA config file: list of HbaLine structs.
  * parsed_hba_context is the memory context where it lives.
  */
@@ -99,6 +126,9 @@ 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);
+static Datum getauthmethod(UserAuth auth_method);
+static Jsonb *gethba_options(HbaLine *hba);
+static void lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason);
 
 /*
  * isblank() exists in the ISO C99 spec, but it's not very portable yet,
@@ -1640,7 +1670,8 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  *	request.
  */
 static void
-check_hba(hbaPort *port)
+check_hba(hbaPort *port, hba_line_callback callback,
+		  void *callback_context)
 {
 	Oid			roleid;
 	ListCell   *line;
@@ -1657,25 +1688,41 @@ check_hba(hbaPort *port)
 		if (hba->conntype == ctLocal)
 		{
 			if (!IS_AF_UNIX(port->raddr.addr.ss_family))
+			{
+				if (callback)
+					callback(callback_context, hba, _("connection type mismatch"));
 				continue;
+			}
 		}
 		else
 		{
 			if (IS_AF_UNIX(port->raddr.addr.ss_family))
+			{
+				if (callback)
+					callback(callback_context, hba, _("connection type mismatch"));
 				continue;
+			}
 
 			/* Check SSL state */
 			if (port->ssl_in_use)
 			{
 				/* Connection is SSL, match both "host" and "hostssl" */
 				if (hba->conntype == ctHostNoSSL)
+				{
+					if (callback)
+						callback(callback_context, hba, _("connection type mismatch"));
 					continue;
+				}
 			}
 			else
 			{
 				/* Connection is not SSL, match both "host" and "hostnossl" */
 				if (hba->conntype == ctHostSSL)
+				{
+					if (callback)
+						callback(callback_context, hba, _("connection type mismatch"));
 					continue;
+				}
 			}
 
 			/* Check IP address */
@@ -1686,14 +1733,22 @@ check_hba(hbaPort *port)
 					{
 						if (!check_hostname(port,
 											hba->hostname))
+						{
+							if (callback)
+								callback(callback_context, hba, _("hostname mismatch"));
 							continue;
+						}
 					}
 					else
 					{
 						if (!check_ip(&port->raddr,
 									  (struct sockaddr *) & hba->addr,
 									  (struct sockaddr *) & hba->mask))
+						{
+							if (callback)
+								callback(callback_context, hba, _("IP address mismatch"));
 							continue;
+						}
 					}
 					break;
 				case ipCmpAll:
@@ -1702,7 +1757,11 @@ check_hba(hbaPort *port)
 				case ipCmpSameNet:
 					if (!check_same_host_or_net(&port->raddr,
 												hba->ip_cmp_method))
+					{
+						if (callback)
+							callback(callback_context, hba, _("samehost/samenet mismatch"));
 						continue;
+					}
 					break;
 				default:
 					/* shouldn't get here, but deem it no-match if so */
@@ -1713,10 +1772,21 @@ check_hba(hbaPort *port)
 		/* Check database and role */
 		if (!check_db(port->database_name, port->user_name, roleid,
 					  hba->databases))
+		{
+			if (callback)
+				callback(callback_context, hba, _("database name mismatch"));
 			continue;
+		}
 
 		if (!check_role(port->user_name, roleid, hba->roles))
+		{
+			if (callback)
+				callback(callback_context, hba, _("user name mismatch"));
 			continue;
+		}
+
+		if (callback)
+			callback(callback_context, hba, NULL);
 
 		/* Found a record that matched! */
 		port->hba = hba;
@@ -1770,8 +1840,7 @@ load_hba(void)
 	FreeFile(file);
 
 	/* Now parse all the lines */
-	Assert(PostmasterContext);
-	hbacxt = AllocSetContextCreate(PostmasterContext,
+	hbacxt = AllocSetContextCreate(CurrentMemoryContext,
 								   "hba parser context",
 								   ALLOCSET_DEFAULT_MINSIZE,
 								   ALLOCSET_DEFAULT_MINSIZE,
@@ -1829,14 +1898,26 @@ load_hba(void)
 	}
 
 	/* Loaded new file successfully, replace the one we use */
-	if (parsed_hba_context != NULL)
-		MemoryContextDelete(parsed_hba_context);
+	discard_hba();
 	parsed_hba_context = hbacxt;
 	parsed_hba_lines = new_parsed_lines;
 
 	return true;
 }
 
+
+void
+discard_hba(void)
+{
+	if (parsed_hba_context != NULL)
+	{
+		MemoryContextDelete(parsed_hba_context);
+		parsed_hba_context = NULL;
+		parsed_hba_lines = NIL;
+	}
+}
+
+
 /*
  * Parse one tokenised line from the ident config file and store the result in
  * an IdentLine structure, or NULL if parsing fails.
@@ -2148,8 +2229,7 @@ load_ident(void)
 	FreeFile(file);
 
 	/* Now parse all the lines */
-	Assert(PostmasterContext);
-	ident_context = AllocSetContextCreate(PostmasterContext,
+	ident_context = AllocSetContextCreate(CurrentMemoryContext,
 										  "ident parser context",
 										  ALLOCSET_DEFAULT_MINSIZE,
 										  ALLOCSET_DEFAULT_MINSIZE,
@@ -2202,6 +2282,20 @@ load_ident(void)
 	}
 
 	/* Loaded new file successfully, replace the one we use */
+	discard_ident();
+	parsed_ident_context = ident_context;
+	parsed_ident_lines = new_parsed_lines;
+
+	return true;
+}
+
+
+void
+discard_ident(void)
+{
+	ListCell   *parsed_line_cell;
+	IdentLine  *newline;
+
 	if (parsed_ident_lines != NIL)
 	{
 		foreach(parsed_line_cell, parsed_ident_lines)
@@ -2210,18 +2304,17 @@ load_ident(void)
 			if (newline->ident_user[0] == '/')
 				pg_regfree(&newline->re);
 		}
+		parsed_ident_lines = NIL;
 	}
+
 	if (parsed_ident_context != NULL)
+	{
 		MemoryContextDelete(parsed_ident_context);
-
-	parsed_ident_context = ident_context;
-	parsed_ident_lines = new_parsed_lines;
-
-	return true;
+		parsed_ident_context = NULL;
+	}
 }
 
 
-
 /*
  *	Determine what authentication method should be used when accessing database
  *	"database" from frontend "raddr", user "user".  Return the method and
@@ -2233,5 +2326,505 @@ load_ident(void)
 void
 hba_getauthmethod(hbaPort *port)
 {
-	check_hba(port);
+	check_hba(port, NULL, NULL);
+}
+
+
+/*
+ * 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 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 uaLDAP:
+			result = CStringGetTextDatum("ldap");
+			break;
+		case uaCert:
+			result = CStringGetTextDatum("cert");
+			break;
+		case uaRADIUS:
+			result = CStringGetTextDatum("radius");
+			break;
+		case uaPeer:
+			result = CStringGetTextDatum("peer");
+			break;
+		default:
+			elog(ERROR, "unexpected authentication method in parsed HBA entry");
+			break;
+	}
+
+	return result;
+}
+
+static Jsonb *
+gethba_options(HbaLine *hba)
+{
+	JsonbParseState *parseState = NULL;
+	JsonbValue *result;
+
+	result = pushJsonbValue(&parseState, WJB_BEGIN_OBJECT, NULL);
+
+	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
+	{
+		if (hba->include_realm)
+		{
+			push_jsonb_string_key(&parseState, "include_realm");
+			push_jsonb_bool_value(&parseState, true);
+		}
+
+		if (hba->krb_realm)
+		{
+			push_jsonb_string_key(&parseState, "krb_realm");
+			push_jsonb_string_value(&parseState, hba->krb_realm);
+		}
+	}
+
+	if (hba->usermap)
+	{
+		push_jsonb_string_key(&parseState, "map");
+		push_jsonb_string_value(&parseState, hba->usermap);
+	}
+
+	if (hba->clientcert)
+	{
+		push_jsonb_string_key(&parseState, "clientcert");
+		push_jsonb_bool_value(&parseState, true);
+	}
+
+	if (hba->pamservice)
+	{
+		push_jsonb_string_key(&parseState, "pamservice");
+		push_jsonb_string_value(&parseState, hba->pamservice);
+	}
+
+	if (hba->auth_method == uaLDAP)
+	{
+		if (hba->ldapserver)
+		{
+			push_jsonb_string_key(&parseState, "ldapserver");
+			push_jsonb_string_value(&parseState, hba->ldapserver);
+		}
+
+		if (hba->ldapport)
+		{
+			push_jsonb_string_key(&parseState, "ldapport");
+			push_jsonb_int32_value(&parseState, hba->ldapport);
+		}
+
+		if (hba->ldaptls)
+		{
+			push_jsonb_string_key(&parseState, "ldaptls");
+			push_jsonb_bool_value(&parseState, true);
+		}
+
+		if (hba->ldapprefix)
+		{
+			push_jsonb_string_key(&parseState, "ldapprefix");
+			push_jsonb_string_value(&parseState, hba->ldapprefix);
+		}
+
+		if (hba->ldapsuffix)
+		{
+			push_jsonb_string_key(&parseState, "ldapsuffix");
+			push_jsonb_string_value(&parseState, hba->ldapsuffix);
+		}
+
+		if (hba->ldapbasedn)
+		{
+			push_jsonb_string_key(&parseState, "ldapbasedn");
+			push_jsonb_string_value(&parseState, hba->ldapbasedn);
+		}
+
+		if (hba->ldapbinddn)
+		{
+			push_jsonb_string_key(&parseState, "ldapbinddn");
+			push_jsonb_string_value(&parseState, hba->ldapbinddn);
+		}
+
+		if (hba->ldapbindpasswd)
+		{
+			push_jsonb_string_key(&parseState, "ldapbindpasswd");
+			push_jsonb_string_value(&parseState, hba->ldapbindpasswd);
+		}
+
+		if (hba->ldapsearchattribute)
+		{
+			push_jsonb_string_key(&parseState, "ldapsearchattribute");
+			push_jsonb_string_value(&parseState, hba->ldapsearchattribute);
+		}
+
+		if (hba->ldapscope)
+		{
+			push_jsonb_string_key(&parseState, "ldapscope");
+			push_jsonb_int32_value(&parseState, hba->ldapscope);
+		}
+	}
+
+	if (hba->auth_method == uaRADIUS)
+	{
+		if (hba->radiusserver)
+		{
+			push_jsonb_string_key(&parseState, "radiusserver");
+			push_jsonb_string_value(&parseState, hba->radiusserver);
+		}
+
+		if (hba->radiussecret)
+		{
+			push_jsonb_string_key(&parseState, "radiussecret");
+			push_jsonb_string_value(&parseState, hba->radiussecret);
+		}
+
+		if (hba->radiusidentifier)
+		{
+			push_jsonb_string_key(&parseState, "radiusidentifier");
+			push_jsonb_string_value(&parseState, hba->radiusidentifier);
+		}
+
+		if (hba->radiusport)
+		{
+			push_jsonb_string_key(&parseState, "radiusport");
+			push_jsonb_int32_value(&parseState, hba->radiusport);
+		}
+	}
+
+	result = pushJsonbValue(&parseState, WJB_END_OBJECT, NULL);
+	return JsonbValueToJsonb(result);
+}
+
+
+static void
+lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason)
+{
+	Datum		values[NUM_PG_HBA_LOOKUP_ATTS];
+	bool		nulls[NUM_PG_HBA_LOOKUP_ATTS];
+	ListCell   *dbcell;
+	char		buffer[NI_MAXHOST];
+	HeapTuple	tuple;
+	int			index;
+	MemoryContext old_cxt;
+	HbalineContext *mycxt;
+
+	mycxt = (HbalineContext *) context;
+
+	index = 0;
+	memset(values, 0, sizeof(values));
+	memset(nulls, 0, sizeof(nulls));
+
+	old_cxt = MemoryContextSwitchTo(mycxt->memcxt);
+
+	/* line_number */
+	values[index] = Int32GetDatum(hba->linenumber);
+
+	/* mode */
+	index++;
+	if (reason == NULL)
+		values[index] = CStringGetTextDatum(_("matched"));
+	else
+		values[index] = CStringGetTextDatum(_("skipped"));
+
+	/* 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;
+		default:
+			elog(ERROR, "unexpected connection type in parsed HBA entry");
+			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);
+		}
+
+		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);
+		}
+
+		values[index] = PointerGetDatum(strlist_to_textarray(roles));
+	}
+	else
+		nulls[index] = true;
+
+	/* address */
+	index++;
+	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+	}
+	else
+		nulls[index] = true;
+
+	/* netmask */
+	index++;
+	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+	}
+	else
+		nulls[index] = true;
+
+	/* hostname */
+	index++;
+	if (hba->hostname)
+		values[index] = CStringGetTextDatum(hba->hostname);
+	else
+		nulls[index] = true;
+
+	/* method */
+	index++;
+	values[index] = getauthmethod(hba->auth_method);
+
+	/* options */
+	index++;
+	values[index] = PointerGetDatum(gethba_options(hba));
+
+	/* reason */
+	index++;
+	if (reason)
+		values[index] = CStringGetTextDatum(reason);
+	else
+		nulls[index] = true;
+
+	tuple = heap_form_tuple(mycxt->tupdesc, values, nulls);
+	tuplestore_puttuple(mycxt->tuple_store, tuple);
+
+	MemoryContextSwitchTo(old_cxt);
+	return;
+}
+
+/*
+ * SQL-accessible SRF to return all the settings from the pg_hba.conf
+ * file.
+ */
+Datum
+pg_hba_lookup(PG_FUNCTION_ARGS)
+{
+	hbaPort    *port;
+	Tuplestorestate *tuple_store;
+	TupleDesc	tupdesc;
+	MemoryContext old_cxt;
+	HbalineContext *mycxt;
+	ReturnSetInfo *rsi;
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to view pg_hba.conf settings"))));
+
+	/*
+	 * 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;
+
+	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+		(rsi->allowedModes & SFRM_Materialize) == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+
+	port = palloc0(sizeof(hbaPort));
+
+	if (PG_ARGISNULL(0))
+		ereport(ERROR,
+				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				 (errmsg("database name is required to match pg_hba configuration entry"))));
+	else
+		port->database_name = TextDatumGetCString(PG_GETARG_DATUM(0));
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				 (errmsg("user name is required to match pg_hba configuration entry"))));
+	else
+		port->user_name = TextDatumGetCString(PG_GETARG_DATUM(1));
+
+
+	if (!PG_ARGISNULL(2))
+	{
+		char	   *address = NULL;
+		struct addrinfo *gai_result = NULL;
+		struct addrinfo hints;
+		int			ret;
+
+		address = TextDatumGetCString(PG_GETARG_DATUM(2));
+
+		/* Get the IP address either way */
+		hints.ai_flags = AI_NUMERICHOST;
+		hints.ai_family = AF_UNSPEC;
+		hints.ai_socktype = 0;
+		hints.ai_protocol = 0;
+		hints.ai_addrlen = 0;
+		hints.ai_canonname = NULL;
+		hints.ai_addr = NULL;
+		hints.ai_next = NULL;
+
+		ret = getaddrinfo(address, NULL, &hints, &gai_result);
+		if (ret == 0 && gai_result)
+			memcpy(&port->raddr.addr, gai_result->ai_addr, gai_result->ai_addrlen);
+		else if (ret == EAI_NONAME)
+		{
+			struct addrinfo *gai_result2 = NULL;
+
+			port->remote_hostname = pstrdup(address);
+
+			ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result2);
+			if (ret == 0 && gai_result2)
+				memcpy(&port->raddr.addr, gai_result2->ai_addr, gai_result2->ai_addrlen);
+			else
+				ereport(ERROR,
+				(errmsg("getaddrinfo failed to look into hostname \"%s\": %s",
+						port->remote_hostname, gai_strerror(ret))));
+
+			if (gai_result2)
+				freeaddrinfo(gai_result2);
+		}
+		else
+			ereport(ERROR,
+					(errmsg("invalid IP address \"%s\": %s",
+							address, gai_strerror(ret))));
+		if (gai_result)
+			freeaddrinfo(gai_result);
+	}
+	else
+		port->raddr.addr.ss_family = AF_UNIX;
+
+	port->ssl_in_use = DatumGetBool(PG_GETARG_DATUM(3));
+
+	rsi->returnMode = SFRM_Materialize;
+
+	if (!load_hba())
+		ereport(ERROR,
+				(errmsg("failed to load pg_hba.conf file"),
+			   errhint("more details may be available in the server log.")));
+
+	/*
+	 * Create the tupledesc and tuplestore in the per_query context as
+	 * required for SFRM_Materialize.
+	 */
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+
+	tupdesc = CreateTemplateTupleDesc(NUM_PG_HBA_LOOKUP_ATTS, false);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "line_number",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "mode",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "type",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "database",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "user_name",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "address",
+					   INETOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "netmask",
+					   INETOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "hostname",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "method",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "options",
+					   JSONBOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "reason",
+					   TEXTOID, -1, 0);
+	BlessTupleDesc(tupdesc);
+
+	tuple_store =
+		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+							  false, work_mem);
+
+	MemoryContextSwitchTo(old_cxt);
+
+	mycxt = (HbalineContext *) palloc(sizeof(HbalineContext));
+	mycxt->memcxt = AllocSetContextCreate(CurrentMemoryContext,
+										  "pg_hba_lookup tuple cxt",
+										  ALLOCSET_DEFAULT_MINSIZE,
+										  ALLOCSET_DEFAULT_INITSIZE,
+										  ALLOCSET_DEFAULT_MAXSIZE);
+	mycxt->tupdesc = tupdesc;
+	mycxt->tuple_store = tuple_store;
+
+	check_hba(port, lookup_hba_line_callback, mycxt);
+
+	MemoryContextDelete(mycxt->memcxt);
+	pfree(mycxt);
+
+	/* Clean up the memory allocated by load_hba() explicitly. */
+	discard_hba();
+
+	rsi->setDesc = tupdesc;
+	rsi->setResult = tuple_store;
+
+	PG_RETURN_NULL();
 }
diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c
index e1ceea6..171e532 100644
--- a/src/backend/utils/adt/jsonb_util.c
+++ b/src/backend/utils/adt/jsonb_util.c
@@ -1802,3 +1802,49 @@ uniqueifyJsonbObject(JsonbValue *object)
 		object->val.object.nPairs = res + 1 - object->val.object.pairs;
 	}
 }
+
+void
+push_jsonb_string_key(JsonbParseState **pstate, char *string_key)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvString;
+	jb.val.string.len = strlen(string_key);
+	jb.val.string.val = pstrdup(string_key);
+	pushJsonbValue(pstate, WJB_KEY, &jb);
+}
+
+void
+push_jsonb_bool_value(JsonbParseState **pstate, bool bool_val)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvBool;
+	jb.val.boolean = bool_val;
+
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
+
+void
+push_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val)
+{
+	JsonbValue	jb;
+	char		outputstr[64];
+
+	snprintf(outputstr, 64, "%d", int32_val);
+	jb.type = jbvNumeric;
+	jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1));
+
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
+
+void
+push_jsonb_string_value(JsonbParseState **pstate, char *string_value)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvString;
+	jb.val.string.len = strlen(string_value);
+	jb.val.string.val = pstrdup(string_value);
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 6b760d4..d92cd9a 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -191,18 +191,6 @@ PerformAuthentication(Port *port)
 	 * FIXME: [fork/exec] Ugh.  Is there a way around this overhead?
 	 */
 #ifdef EXEC_BACKEND
-	/*
-	 * load_hba() and load_ident() want to work within the PostmasterContext,
-	 * so create that if it doesn't exist (which it won't).  We'll delete it
-	 * again later, in PostgresMain.
-	 */
-	if (PostmasterContext == NULL)
-		PostmasterContext = AllocSetContextCreate(TopMemoryContext,
-												  "Postmaster",
-												  ALLOCSET_DEFAULT_MINSIZE,
-												  ALLOCSET_DEFAULT_INITSIZE,
-												  ALLOCSET_DEFAULT_MAXSIZE);
-
 	if (!load_hba())
 	{
 		/*
@@ -736,6 +724,14 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 	}
 
 	/*
+	 * We don't need the HBA and ident data going forward, but we can't rely
+	 * on release of PostmasterContext to clean that up, so discard them
+	 * explicitly here.
+	 */
+	discard_hba();
+	discard_ident();
+
+	/*
 	 * If we're trying to shut down, only superusers can connect, and new
 	 * replication connections are not allowed.
 	 */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index aec6c4c..3253382 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3010,6 +3010,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 = 3997 (  pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f f t v u 4 0 2249 "25 25 25 16" "{25,25,25,16,23,25,25,1009,1009,869,869,25,25,3802,25}" "{i,i,i,i,o,o,o,o,o,o,o,o,o,o,o}" "{database,user_name,address,ssl_inuse,line_number,mode,type,database,user_name,address,netmask,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup _null_ _null_ _null_));
+DESCR("view client authentication settings");
 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 68a953a..291300b 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -98,6 +98,10 @@ typedef struct Port hbaPort;
 
 extern bool load_hba(void);
 extern bool load_ident(void);
+
+extern void discard_hba(void);
+extern void discard_ident(void);
+
 extern void hba_getauthmethod(hbaPort *port);
 extern int check_usermap(const char *usermap_name,
 			  const char *pg_role, const char *auth_user,
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 7ec93c9..24625e5 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1151,6 +1151,9 @@ extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
 /* pg_config.c */
 extern Datum pg_config(PG_FUNCTION_ARGS);
 
+/* hba.c */
+extern Datum pg_hba_lookup(PG_FUNCTION_ARGS);
+
 /* rls.c */
 extern Datum row_security_active(PG_FUNCTION_ARGS);
 extern Datum row_security_active_name(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 5f49d8d..33e00d2 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -427,6 +427,12 @@ extern bool JsonbDeepContains(JsonbIterator **val,
 				  JsonbIterator **mContained);
 extern void JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash);
 
+/* jsonb_util.c support functions */
+void push_jsonb_string_key(JsonbParseState **pstate, char *string_key);
+void push_jsonb_bool_value(JsonbParseState **pstate, bool bool_val);
+void push_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val);
+void push_jsonb_string_value(JsonbParseState **pstate, char *string_value);
+
 /* jsonb.c support functions */
 extern char *JsonbToCString(StringInfo out, JsonbContainer *in,
 			   int estimated_len);
#54David Steele
david@pgmasters.net
In reply to: Haribabu Kommi (#53)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On 3/3/16 12:16 AM, Haribabu Kommi wrote:

On Fri, Feb 5, 2016 at 2:29 PM, Haribabu Kommi <kommi.haribabu@gmail.com> wrote:

This patch needs to be applied on top discard_hba_and_ident_cxt patch
that is posted earlier.

Here I attached a re-based patch to the latest head with inclusion of
discard_hba_ident_cxt patch for easier review as a single patch.

Alex, Scott, do you have an idea of when you'll be able to review this
new version?

It applies with a minor conflict (caused by pg_control_* commit):

$ git apply -3 ../other/pg_hba_lookup_poc_v13.patch
error: patch failed: src/include/utils/builtins.h:1151
Falling back to three-way merge...
Applied patch to 'src/include/utils/builtins.h' with conflicts.
U src/include/utils/builtins.h

Thanks,
--
-David
david@pgmasters.net

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#55Shulgin, Oleksandr
oleksandr.shulgin@zalando.de
In reply to: David Steele (#54)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Tue, Mar 15, 2016 at 7:23 PM, David Steele <david@pgmasters.net> wrote:

On 3/3/16 12:16 AM, Haribabu Kommi wrote:

On Fri, Feb 5, 2016 at 2:29 PM, Haribabu Kommi <kommi.haribabu@gmail.com>

wrote:

This patch needs to be applied on top discard_hba_and_ident_cxt patch
that is posted earlier.

Here I attached a re-based patch to the latest head with inclusion of
discard_hba_ident_cxt patch for easier review as a single patch.

Alex, Scott, do you have an idea of when you'll be able to review this
new version?

The new version applies with some fuzziness to the current master and
compiles cleanly.

Some comments:

+/* Context to use with hba_line_callback function. */
+typedef struct
+{
+   MemoryContext memcxt;
+   TupleDesc   tupdesc;
+   Tuplestorestate *tuple_store;
+}  HbalineContext;

Rather "with *lookup_hba_line_callback*", as hba_line_callback() is a
generic one.

+ line_number |  mode   | type  | database | user_name |  address  |
          netmask                 | hostname | method | options |
 reason
+-------------+---------+-------+----------+-----------+-----------+-----------------------------------------+----------+--------+---------+--------------------------
+          84 | skipped | local | {all}    | {all}     |           |
                                  |          | trust  | {}      |
connection type mismatch
+          86 | skipped | host  | {all}    | {all}     | 127.0.0.1 |
255.255.255.255                         |          | trust  | {}      | IP
address mismatch
+          88 | matched | host  | {all}    | {all}     | ::1       |
ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff |          | trust  | {}      |

Hm... now I'm not sure if we really need the "mode" column. It should be
clear that we skipped every line that had a non-NULL "reason". I guess we
could remove "mode" and rename "reason" to "skip_reason"?

Still remains an issue of representing special keywords in database and
user_name fields, but there was no consensus about that though.

--
Alex

#56Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Shulgin, Oleksandr (#55)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Wed, Mar 16, 2016 at 9:49 PM, Shulgin, Oleksandr
<oleksandr.shulgin@zalando.de> wrote:

On Tue, Mar 15, 2016 at 7:23 PM, David Steele <david@pgmasters.net> wrote:

On 3/3/16 12:16 AM, Haribabu Kommi wrote:

On Fri, Feb 5, 2016 at 2:29 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

This patch needs to be applied on top discard_hba_and_ident_cxt patch
that is posted earlier.

Here I attached a re-based patch to the latest head with inclusion of
discard_hba_ident_cxt patch for easier review as a single patch.

Alex, Scott, do you have an idea of when you'll be able to review this
new version?

The new version applies with some fuzziness to the current master and
compiles cleanly.

Some comments:

+/* Context to use with hba_line_callback function. */
+typedef struct
+{
+   MemoryContext memcxt;
+   TupleDesc   tupdesc;
+   Tuplestorestate *tuple_store;
+}  HbalineContext;

Rather "with *lookup_hba_line_callback*", as hba_line_callback() is a
generic one.

Fine. I will change the function and context names.

+ line_number |  mode   | type  | database | user_name |  address  |
netmask                 | hostname | method | options |          reason
+-------------+---------+-------+----------+-----------+-----------+-----------------------------------------+----------+--------+---------+--------------------------
+          84 | skipped | local | {all}    | {all}     |           |
|          | trust  | {}      | connection type mismatch
+          86 | skipped | host  | {all}    | {all}     | 127.0.0.1 |
255.255.255.255                         |          | trust  | {}      | IP
address mismatch
+          88 | matched | host  | {all}    | {all}     | ::1       |
ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff |          | trust  | {}      |

Hm... now I'm not sure if we really need the "mode" column. It should be
clear that we skipped every line that had a non-NULL "reason". I guess we
could remove "mode" and rename "reason" to "skip_reason"?

Ok. Lets hear from others also regarding the same.

Still remains an issue of representing special keywords in database and
user_name fields, but there was no consensus about that though.

How about adding keyword_database and keyword_user columns to listing
out the keywords. These columns will be populated only when the hba line
contains any keywords.

Regards,
Hari Babu
Fujitsu Australia

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#57Shulgin, Oleksandr
oleksandr.shulgin@zalando.de
In reply to: Haribabu Kommi (#56)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Thu, Mar 17, 2016 at 2:12 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Wed, Mar 16, 2016 at 9:49 PM, Shulgin, Oleksandr
<oleksandr.shulgin@zalando.de> wrote:

Some comments:

+/* Context to use with hba_line_callback function. */
+typedef struct
+{
+   MemoryContext memcxt;
+   TupleDesc   tupdesc;
+   Tuplestorestate *tuple_store;
+}  HbalineContext;

Rather "with *lookup_hba_line_callback*", as hba_line_callback() is a
generic one.

Fine. I will change the function and context names.

You mean change context name and correct the comment? I didn't suggest to
change the function name.

Still remains an issue of representing special keywords in database and

user_name fields, but there was no consensus about that though.

How about adding keyword_database and keyword_user columns to listing
out the keywords. These columns will be populated only when the hba line
contains any keywords.

Hm... that could work too.

--
Alex

#58Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Shulgin, Oleksandr (#57)
1 attachment(s)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Thu, Mar 17, 2016 at 6:56 PM, Shulgin, Oleksandr
<oleksandr.shulgin@zalando.de> wrote:

On Thu, Mar 17, 2016 at 2:12 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Wed, Mar 16, 2016 at 9:49 PM, Shulgin, Oleksandr
<oleksandr.shulgin@zalando.de> wrote:

Some comments:

+/* Context to use with hba_line_callback function. */
+typedef struct
+{
+   MemoryContext memcxt;
+   TupleDesc   tupdesc;
+   Tuplestorestate *tuple_store;
+}  HbalineContext;

Rather "with *lookup_hba_line_callback*", as hba_line_callback() is a
generic one.

Fine. I will change the function and context names.

You mean change context name and correct the comment? I didn't suggest to
change the function name.

Changed the context name and the comment only.

Still remains an issue of representing special keywords in database and
user_name fields, but there was no consensus about that though.

How about adding keyword_database and keyword_user columns to listing
out the keywords. These columns will be populated only when the hba line
contains any keywords.

Hm... that could work too.

Here I attached patch with the added two keyword columns.
During the testing with different IP comparison methods like 'samehost' or
'samenet', the address details are not displayed. Is there any need of
showing the IP compare method also?

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg-hba-lookup-18-03-2016.patchapplication/octet-stream; name=pg-hba-lookup-18-03-2016.patchDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 000489d..9fa8296 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17194,6 +17194,19 @@ SELECT collation for ('foo' COLLATE "de_DE");
        <entry><type>text</type></entry>
        <entry>set parameter and return new value</entry>
       </row>
+      <row>
+       <entry>
+        <indexterm>
+         <primary>pg_hba_lookup</primary>
+        </indexterm>
+        <literal><function>pg_hba_lookup(<parameter>database</> <type>text</>,
+                            <parameter>user_name</> <type>text</>
+                            [, <parameter>address</> <type>text</>]
+                            [, <parameter>ssl_inuse</> <type>text</>)</function></literal>
+       </entry>
+       <entry><type>record</type></entry>
+       <entry>scan <filename>pg_hba.conf</> and return examined entries up to a matching one</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
@@ -17251,6 +17264,32 @@ SELECT set_config('log_statement_stats', 'off', false);
 </programlisting>
    </para>
 
+   <para>
+    <function>pg_hba_lookup</function> returns a set of records containing the
+    line number, mode, type, database, user_name, address, netmask, hostname,
+    method, options and skip reason. For example, to debug problems with user
+    <literal>kommih</> trying to connect to a database <literal>postgres</>
+    from IPv6-address <literal>::1</>, one can issue a following query:
+<programlisting>
+postgres=# select * from pg_hba_lookup('postgres', 'kommih', '::1');
+ line_number |  mode   | type  | keyword_database | database | keyword_user | user_name |  address  |                 netmask                 | hostname | method | options |          reason          
+-------------+---------+-------+------------------+----------+--------------+-----------+-----------+-----------------------------------------+----------+--------+---------+--------------------------
+          84 | skipped | local | {all}            |          | {all}        |           |           |                                         |          | trust  | {}      | connection type mismatch
+          86 | skipped | host  | {all}            |          | {all}        |           | 127.0.0.1 | 255.255.255.255                         |          | trust  | {}      | IP address mismatch
+          90 | matched | host  | {all}            |          | {all}        |           | ::1       | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff |          | trust  | {}      | 
+(3 rows)
+
+</programlisting>
+    This function actually loads the contents of <filename>pg_hba.conf</> file
+    into memory to perform matching, thus a database administrator can use it
+    to test the effects of changes made to the file in isolation prior to
+    actually applying the configuration cluster-wide with a signal to the
+    postmaster process.
+
+    Only superusers can access this function to look the
+    <filename>pg_hba.conf</> entries up.
+   </para>
+   
   </sect2>
 
   <sect2 id="functions-admin-signal">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index fef67bd..0f45b75 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -990,3 +990,13 @@ RETURNS jsonb
 LANGUAGE INTERNAL
 STRICT IMMUTABLE
 AS 'jsonb_set';
+
+CREATE OR REPLACE FUNCTION pg_hba_lookup(IN database text, IN user_name text,
+    IN address text DEFAULT NULL, IN ssl_inuse boolean DEFAULT false,
+	OUT line_number int, OUT mode text, OUT type text, OUT keyword_database text[],
+	OUT database text[], OUT keyword_user text[], OUT user_name text[],
+	OUT address inet, OUT netmask inet, OUT hostname text, OUT method text,
+	OUT options jsonb, OUT reason text)
+RETURNS SETOF RECORD
+LANGUAGE INTERNAL
+AS 'pg_hba_lookup';
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 28f9fb5..1cbc853 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -25,15 +25,22 @@
 #include <arpa/inet.h>
 #include <unistd.h>
 
+#include "access/htup_details.h"
+#include "catalog/objectaddress.h"
 #include "catalog/pg_collation.h"
+#include "catalog/pg_type.h"
+#include "funcapi.h"
 #include "libpq/ip.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/jsonb.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 
@@ -52,6 +59,8 @@
 #define MAX_TOKEN	256
 #define MAX_LINE	8192
 
+#define NUM_PG_HBA_LOOKUP_ATTS	 13
+
 /* callback data for check_network_callback */
 typedef struct check_network_data
 {
@@ -75,6 +84,24 @@ typedef struct HbaToken
 } HbaToken;
 
 /*
+ * Optional callback function type for check_hba() function.
+ * Currently, valid callback function is passed to check_hba()
+ * by pg_hba_lookup system function to frame the hba tuple row
+ * based on inputs provided by the user.
+ */
+typedef void (*hba_line_callback) (void *context, HbaLine *hba_line,
+											   const char *reason);
+
+/* Context to use with lookup_hba_line_callback function. */
+typedef struct
+{
+	MemoryContext memcxt;
+	TupleDesc	tupdesc;
+	Tuplestorestate *tuple_store;
+}	lookup_hba_line_context;
+
+
+/*
  * pre-parsed content of HBA config file: list of HbaLine structs.
  * parsed_hba_context is the memory context where it lives.
  */
@@ -99,6 +126,10 @@ 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);
+static Datum getauthmethod(UserAuth auth_method);
+static Jsonb *gethba_options(HbaLine *hba);
+static void lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason);
+static bool token_is_a_database_keyword(HbaToken *tok);
 
 /*
  * isblank() exists in the ISO C99 spec, but it's not very portable yet,
@@ -541,6 +572,18 @@ check_db(const char *dbname, const char *role, Oid roleid, List *tokens)
 }
 
 static bool
+token_is_a_database_keyword(HbaToken *tok)
+{
+	if (token_is_keyword(tok, "replication")
+		|| token_is_keyword(tok, "all")
+		|| token_is_keyword(tok, "sameuser")
+		|| token_is_keyword(tok, "samegroup")
+		|| token_is_keyword(tok, "samerole"))
+		return true;
+	return false;
+}
+
+static bool
 ipv4eq(struct sockaddr_in * a, struct sockaddr_in * b)
 {
 	return (a->sin_addr.s_addr == b->sin_addr.s_addr);
@@ -1640,7 +1683,8 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  *	request.
  */
 static void
-check_hba(hbaPort *port)
+check_hba(hbaPort *port, hba_line_callback callback,
+		  void *callback_context)
 {
 	Oid			roleid;
 	ListCell   *line;
@@ -1657,25 +1701,41 @@ check_hba(hbaPort *port)
 		if (hba->conntype == ctLocal)
 		{
 			if (!IS_AF_UNIX(port->raddr.addr.ss_family))
+			{
+				if (callback)
+					callback(callback_context, hba, _("connection type mismatch"));
 				continue;
+			}
 		}
 		else
 		{
 			if (IS_AF_UNIX(port->raddr.addr.ss_family))
+			{
+				if (callback)
+					callback(callback_context, hba, _("connection type mismatch"));
 				continue;
+			}
 
 			/* Check SSL state */
 			if (port->ssl_in_use)
 			{
 				/* Connection is SSL, match both "host" and "hostssl" */
 				if (hba->conntype == ctHostNoSSL)
+				{
+					if (callback)
+						callback(callback_context, hba, _("connection type mismatch"));
 					continue;
+				}
 			}
 			else
 			{
 				/* Connection is not SSL, match both "host" and "hostnossl" */
 				if (hba->conntype == ctHostSSL)
+				{
+					if (callback)
+						callback(callback_context, hba, _("connection type mismatch"));
 					continue;
+				}
 			}
 
 			/* Check IP address */
@@ -1686,14 +1746,22 @@ check_hba(hbaPort *port)
 					{
 						if (!check_hostname(port,
 											hba->hostname))
+						{
+							if (callback)
+								callback(callback_context, hba, _("hostname mismatch"));
 							continue;
+						}
 					}
 					else
 					{
 						if (!check_ip(&port->raddr,
 									  (struct sockaddr *) & hba->addr,
 									  (struct sockaddr *) & hba->mask))
+						{
+							if (callback)
+								callback(callback_context, hba, _("IP address mismatch"));
 							continue;
+						}
 					}
 					break;
 				case ipCmpAll:
@@ -1702,7 +1770,11 @@ check_hba(hbaPort *port)
 				case ipCmpSameNet:
 					if (!check_same_host_or_net(&port->raddr,
 												hba->ip_cmp_method))
+					{
+						if (callback)
+							callback(callback_context, hba, _("samehost/samenet mismatch"));
 						continue;
+					}
 					break;
 				default:
 					/* shouldn't get here, but deem it no-match if so */
@@ -1713,10 +1785,21 @@ check_hba(hbaPort *port)
 		/* Check database and role */
 		if (!check_db(port->database_name, port->user_name, roleid,
 					  hba->databases))
+		{
+			if (callback)
+				callback(callback_context, hba, _("database name mismatch"));
 			continue;
+		}
 
 		if (!check_role(port->user_name, roleid, hba->roles))
+		{
+			if (callback)
+				callback(callback_context, hba, _("user name mismatch"));
 			continue;
+		}
+
+		if (callback)
+			callback(callback_context, hba, NULL);
 
 		/* Found a record that matched! */
 		port->hba = hba;
@@ -1770,8 +1853,7 @@ load_hba(void)
 	FreeFile(file);
 
 	/* Now parse all the lines */
-	Assert(PostmasterContext);
-	hbacxt = AllocSetContextCreate(PostmasterContext,
+	hbacxt = AllocSetContextCreate(CurrentMemoryContext,
 								   "hba parser context",
 								   ALLOCSET_DEFAULT_MINSIZE,
 								   ALLOCSET_DEFAULT_MINSIZE,
@@ -1829,14 +1911,26 @@ load_hba(void)
 	}
 
 	/* Loaded new file successfully, replace the one we use */
-	if (parsed_hba_context != NULL)
-		MemoryContextDelete(parsed_hba_context);
+	discard_hba();
 	parsed_hba_context = hbacxt;
 	parsed_hba_lines = new_parsed_lines;
 
 	return true;
 }
 
+
+void
+discard_hba(void)
+{
+	if (parsed_hba_context != NULL)
+	{
+		MemoryContextDelete(parsed_hba_context);
+		parsed_hba_context = NULL;
+		parsed_hba_lines = NIL;
+	}
+}
+
+
 /*
  * Parse one tokenised line from the ident config file and store the result in
  * an IdentLine structure, or NULL if parsing fails.
@@ -2148,8 +2242,7 @@ load_ident(void)
 	FreeFile(file);
 
 	/* Now parse all the lines */
-	Assert(PostmasterContext);
-	ident_context = AllocSetContextCreate(PostmasterContext,
+	ident_context = AllocSetContextCreate(CurrentMemoryContext,
 										  "ident parser context",
 										  ALLOCSET_DEFAULT_MINSIZE,
 										  ALLOCSET_DEFAULT_MINSIZE,
@@ -2202,6 +2295,20 @@ load_ident(void)
 	}
 
 	/* Loaded new file successfully, replace the one we use */
+	discard_ident();
+	parsed_ident_context = ident_context;
+	parsed_ident_lines = new_parsed_lines;
+
+	return true;
+}
+
+
+void
+discard_ident(void)
+{
+	ListCell   *parsed_line_cell;
+	IdentLine  *newline;
+
 	if (parsed_ident_lines != NIL)
 	{
 		foreach(parsed_line_cell, parsed_ident_lines)
@@ -2210,18 +2317,17 @@ load_ident(void)
 			if (newline->ident_user[0] == '/')
 				pg_regfree(&newline->re);
 		}
+		parsed_ident_lines = NIL;
 	}
+
 	if (parsed_ident_context != NULL)
+	{
 		MemoryContextDelete(parsed_ident_context);
-
-	parsed_ident_context = ident_context;
-	parsed_ident_lines = new_parsed_lines;
-
-	return true;
+		parsed_ident_context = NULL;
+	}
 }
 
 
-
 /*
  *	Determine what authentication method should be used when accessing database
  *	"database" from frontend "raddr", user "user".  Return the method and
@@ -2233,5 +2339,543 @@ load_ident(void)
 void
 hba_getauthmethod(hbaPort *port)
 {
-	check_hba(port);
+	check_hba(port, NULL, NULL);
+}
+
+
+/*
+ * 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 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 uaLDAP:
+			result = CStringGetTextDatum("ldap");
+			break;
+		case uaCert:
+			result = CStringGetTextDatum("cert");
+			break;
+		case uaRADIUS:
+			result = CStringGetTextDatum("radius");
+			break;
+		case uaPeer:
+			result = CStringGetTextDatum("peer");
+			break;
+		default:
+			elog(ERROR, "unexpected authentication method in parsed HBA entry");
+			break;
+	}
+
+	return result;
+}
+
+static Jsonb *
+gethba_options(HbaLine *hba)
+{
+	JsonbParseState *parseState = NULL;
+	JsonbValue *result;
+
+	result = pushJsonbValue(&parseState, WJB_BEGIN_OBJECT, NULL);
+
+	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
+	{
+		if (hba->include_realm)
+		{
+			push_jsonb_string_key(&parseState, "include_realm");
+			push_jsonb_bool_value(&parseState, true);
+		}
+
+		if (hba->krb_realm)
+		{
+			push_jsonb_string_key(&parseState, "krb_realm");
+			push_jsonb_string_value(&parseState, hba->krb_realm);
+		}
+	}
+
+	if (hba->usermap)
+	{
+		push_jsonb_string_key(&parseState, "map");
+		push_jsonb_string_value(&parseState, hba->usermap);
+	}
+
+	if (hba->clientcert)
+	{
+		push_jsonb_string_key(&parseState, "clientcert");
+		push_jsonb_bool_value(&parseState, true);
+	}
+
+	if (hba->pamservice)
+	{
+		push_jsonb_string_key(&parseState, "pamservice");
+		push_jsonb_string_value(&parseState, hba->pamservice);
+	}
+
+	if (hba->auth_method == uaLDAP)
+	{
+		if (hba->ldapserver)
+		{
+			push_jsonb_string_key(&parseState, "ldapserver");
+			push_jsonb_string_value(&parseState, hba->ldapserver);
+		}
+
+		if (hba->ldapport)
+		{
+			push_jsonb_string_key(&parseState, "ldapport");
+			push_jsonb_int32_value(&parseState, hba->ldapport);
+		}
+
+		if (hba->ldaptls)
+		{
+			push_jsonb_string_key(&parseState, "ldaptls");
+			push_jsonb_bool_value(&parseState, true);
+		}
+
+		if (hba->ldapprefix)
+		{
+			push_jsonb_string_key(&parseState, "ldapprefix");
+			push_jsonb_string_value(&parseState, hba->ldapprefix);
+		}
+
+		if (hba->ldapsuffix)
+		{
+			push_jsonb_string_key(&parseState, "ldapsuffix");
+			push_jsonb_string_value(&parseState, hba->ldapsuffix);
+		}
+
+		if (hba->ldapbasedn)
+		{
+			push_jsonb_string_key(&parseState, "ldapbasedn");
+			push_jsonb_string_value(&parseState, hba->ldapbasedn);
+		}
+
+		if (hba->ldapbinddn)
+		{
+			push_jsonb_string_key(&parseState, "ldapbinddn");
+			push_jsonb_string_value(&parseState, hba->ldapbinddn);
+		}
+
+		if (hba->ldapbindpasswd)
+		{
+			push_jsonb_string_key(&parseState, "ldapbindpasswd");
+			push_jsonb_string_value(&parseState, hba->ldapbindpasswd);
+		}
+
+		if (hba->ldapsearchattribute)
+		{
+			push_jsonb_string_key(&parseState, "ldapsearchattribute");
+			push_jsonb_string_value(&parseState, hba->ldapsearchattribute);
+		}
+
+		if (hba->ldapscope)
+		{
+			push_jsonb_string_key(&parseState, "ldapscope");
+			push_jsonb_int32_value(&parseState, hba->ldapscope);
+		}
+	}
+
+	if (hba->auth_method == uaRADIUS)
+	{
+		if (hba->radiusserver)
+		{
+			push_jsonb_string_key(&parseState, "radiusserver");
+			push_jsonb_string_value(&parseState, hba->radiusserver);
+		}
+
+		if (hba->radiussecret)
+		{
+			push_jsonb_string_key(&parseState, "radiussecret");
+			push_jsonb_string_value(&parseState, hba->radiussecret);
+		}
+
+		if (hba->radiusidentifier)
+		{
+			push_jsonb_string_key(&parseState, "radiusidentifier");
+			push_jsonb_string_value(&parseState, hba->radiusidentifier);
+		}
+
+		if (hba->radiusport)
+		{
+			push_jsonb_string_key(&parseState, "radiusport");
+			push_jsonb_int32_value(&parseState, hba->radiusport);
+		}
+	}
+
+	result = pushJsonbValue(&parseState, WJB_END_OBJECT, NULL);
+	return JsonbValueToJsonb(result);
+}
+
+
+static void
+lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason)
+{
+	Datum		values[NUM_PG_HBA_LOOKUP_ATTS];
+	bool		nulls[NUM_PG_HBA_LOOKUP_ATTS];
+	ListCell   *dbcell;
+	char		buffer[NI_MAXHOST];
+	HeapTuple	tuple;
+	int			index;
+	MemoryContext old_cxt;
+	lookup_hba_line_context *mycxt;
+
+	mycxt = (lookup_hba_line_context *) context;
+
+	index = 0;
+	memset(values, 0, sizeof(values));
+	memset(nulls, 0, sizeof(nulls));
+
+	old_cxt = MemoryContextSwitchTo(mycxt->memcxt);
+
+	/* line_number */
+	values[index] = Int32GetDatum(hba->linenumber);
+
+	/* mode */
+	index++;
+	if (reason == NULL)
+		values[index] = CStringGetTextDatum(_("matched"));
+	else
+		values[index] = CStringGetTextDatum(_("skipped"));
+
+	/* 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;
+		default:
+			elog(ERROR, "unexpected connection type in parsed HBA entry");
+			break;
+	}
+
+	/* database */
+	index++;
+	if (list_length(hba->databases) != 0)
+	{
+		List	   *names = NULL;
+		List	   *keyword_names = NULL;
+		HbaToken   *tok;
+
+		foreach(dbcell, hba->databases)
+		{
+			tok = lfirst(dbcell);
+			if (token_is_a_database_keyword(tok))
+				keyword_names = lappend(keyword_names, tok->string);
+			else
+				names = lappend(names, tok->string);
+		}
+
+		if (keyword_names != NULL)
+			values[index] = PointerGetDatum(strlist_to_textarray(keyword_names));
+		else
+			nulls[index] = true;
+
+		index++;
+		if (names != NULL)
+			values[index] = PointerGetDatum(strlist_to_textarray(names));
+		else
+			nulls[index] = true;
+	}
+	else
+	{
+		nulls[index] = true;
+		index++;
+		nulls[index] = true;
+	}
+
+	/* user */
+	index++;
+	if (list_length(hba->roles) != 0)
+	{
+		List	   *roles = NULL;
+		List	   *keyword_roles = NULL;
+		HbaToken   *tok;
+
+		foreach(dbcell, hba->roles)
+		{
+			tok = lfirst(dbcell);
+			if (!tok->quoted && strcmp(tok->string, "all") == 0)
+				keyword_roles = lappend(keyword_roles, tok->string);
+			else
+				roles = lappend(roles, tok->string);
+		}
+
+		if (keyword_roles != NULL)
+			values[index] = PointerGetDatum(strlist_to_textarray(keyword_roles));
+		else
+			nulls[index] = true;
+
+		index++;
+		if (roles != NULL)
+			values[index] = PointerGetDatum(strlist_to_textarray(roles));
+		else
+			nulls[index] = true;
+	}
+	else
+	{
+		nulls[index] = true;
+		index++;
+		nulls[index] = true;
+	}
+
+	/* address */
+	index++;
+	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+	}
+	else
+		nulls[index] = true;
+
+	/* netmask */
+	index++;
+	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+	}
+	else
+		nulls[index] = true;
+
+	/* hostname */
+	index++;
+	if (hba->hostname)
+		values[index] = CStringGetTextDatum(hba->hostname);
+	else
+		nulls[index] = true;
+
+	/* method */
+	index++;
+	values[index] = getauthmethod(hba->auth_method);
+
+	/* options */
+	index++;
+	values[index] = PointerGetDatum(gethba_options(hba));
+
+	/* reason */
+	index++;
+	if (reason)
+		values[index] = CStringGetTextDatum(reason);
+	else
+		nulls[index] = true;
+
+	tuple = heap_form_tuple(mycxt->tupdesc, values, nulls);
+	tuplestore_puttuple(mycxt->tuple_store, tuple);
+
+	MemoryContextSwitchTo(old_cxt);
+	return;
+}
+
+/*
+ * SQL-accessible SRF to return all the settings from the pg_hba.conf
+ * file.
+ */
+Datum
+pg_hba_lookup(PG_FUNCTION_ARGS)
+{
+	hbaPort    *port;
+	Tuplestorestate *tuple_store;
+	TupleDesc	tupdesc;
+	MemoryContext old_cxt;
+	lookup_hba_line_context *mycxt;
+	ReturnSetInfo *rsi;
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to view pg_hba.conf settings"))));
+
+	/*
+	 * 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;
+
+	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+		(rsi->allowedModes & SFRM_Materialize) == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+
+	port = palloc0(sizeof(hbaPort));
+
+	if (PG_ARGISNULL(0))
+		ereport(ERROR,
+				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				 (errmsg("database name is required to match pg_hba configuration entry"))));
+	else
+		port->database_name = TextDatumGetCString(PG_GETARG_DATUM(0));
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				 (errmsg("user name is required to match pg_hba configuration entry"))));
+	else
+		port->user_name = TextDatumGetCString(PG_GETARG_DATUM(1));
+
+
+	if (!PG_ARGISNULL(2))
+	{
+		char	   *address = NULL;
+		struct addrinfo *gai_result = NULL;
+		struct addrinfo hints;
+		int			ret;
+
+		address = TextDatumGetCString(PG_GETARG_DATUM(2));
+
+		/* Get the IP address either way */
+		hints.ai_flags = AI_NUMERICHOST;
+		hints.ai_family = AF_UNSPEC;
+		hints.ai_socktype = 0;
+		hints.ai_protocol = 0;
+		hints.ai_addrlen = 0;
+		hints.ai_canonname = NULL;
+		hints.ai_addr = NULL;
+		hints.ai_next = NULL;
+
+		ret = getaddrinfo(address, NULL, &hints, &gai_result);
+		if (ret == 0 && gai_result)
+			memcpy(&port->raddr.addr, gai_result->ai_addr, gai_result->ai_addrlen);
+		else if (ret == EAI_NONAME)
+		{
+			struct addrinfo *gai_result2 = NULL;
+
+			port->remote_hostname = pstrdup(address);
+
+			ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result2);
+			if (ret == 0 && gai_result2)
+				memcpy(&port->raddr.addr, gai_result2->ai_addr, gai_result2->ai_addrlen);
+			else
+				ereport(ERROR,
+				(errmsg("getaddrinfo failed to look into hostname \"%s\": %s",
+						port->remote_hostname, gai_strerror(ret))));
+
+			if (gai_result2)
+				freeaddrinfo(gai_result2);
+		}
+		else
+			ereport(ERROR,
+					(errmsg("invalid IP address \"%s\": %s",
+							address, gai_strerror(ret))));
+		if (gai_result)
+			freeaddrinfo(gai_result);
+	}
+	else
+		port->raddr.addr.ss_family = AF_UNIX;
+
+	port->ssl_in_use = DatumGetBool(PG_GETARG_DATUM(3));
+
+	rsi->returnMode = SFRM_Materialize;
+
+	if (!load_hba())
+		ereport(ERROR,
+				(errmsg("failed to load pg_hba.conf file"),
+			   errhint("more details may be available in the server log.")));
+
+	/*
+	 * Create the tupledesc and tuplestore in the per_query context as
+	 * required for SFRM_Materialize.
+	 */
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+
+	tupdesc = CreateTemplateTupleDesc(NUM_PG_HBA_LOOKUP_ATTS, false);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "line_number",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "mode",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "type",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "keyword_database",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "database",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "keyword_user",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "user_name",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "address",
+					   INETOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "netmask",
+					   INETOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "hostname",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "method",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 12, "options",
+					   JSONBOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 13, "reason",
+					   TEXTOID, -1, 0);
+	BlessTupleDesc(tupdesc);
+
+	tuple_store =
+		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+							  false, work_mem);
+
+	MemoryContextSwitchTo(old_cxt);
+
+	mycxt = (lookup_hba_line_context *) palloc(sizeof(lookup_hba_line_context));
+	mycxt->memcxt = AllocSetContextCreate(CurrentMemoryContext,
+										  "pg_hba_lookup tuple cxt",
+										  ALLOCSET_DEFAULT_MINSIZE,
+										  ALLOCSET_DEFAULT_INITSIZE,
+										  ALLOCSET_DEFAULT_MAXSIZE);
+	mycxt->tupdesc = tupdesc;
+	mycxt->tuple_store = tuple_store;
+
+	check_hba(port, lookup_hba_line_callback, mycxt);
+
+	MemoryContextDelete(mycxt->memcxt);
+	pfree(mycxt);
+
+	/* Clean up the memory allocated by load_hba() explicitly. */
+	discard_hba();
+
+	rsi->setDesc = tupdesc;
+	rsi->setResult = tuple_store;
+
+	PG_RETURN_NULL();
 }
diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c
index e1ceea6..171e532 100644
--- a/src/backend/utils/adt/jsonb_util.c
+++ b/src/backend/utils/adt/jsonb_util.c
@@ -1802,3 +1802,49 @@ uniqueifyJsonbObject(JsonbValue *object)
 		object->val.object.nPairs = res + 1 - object->val.object.pairs;
 	}
 }
+
+void
+push_jsonb_string_key(JsonbParseState **pstate, char *string_key)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvString;
+	jb.val.string.len = strlen(string_key);
+	jb.val.string.val = pstrdup(string_key);
+	pushJsonbValue(pstate, WJB_KEY, &jb);
+}
+
+void
+push_jsonb_bool_value(JsonbParseState **pstate, bool bool_val)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvBool;
+	jb.val.boolean = bool_val;
+
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
+
+void
+push_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val)
+{
+	JsonbValue	jb;
+	char		outputstr[64];
+
+	snprintf(outputstr, 64, "%d", int32_val);
+	jb.type = jbvNumeric;
+	jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1));
+
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
+
+void
+push_jsonb_string_value(JsonbParseState **pstate, char *string_value)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvString;
+	jb.val.string.len = strlen(string_value);
+	jb.val.string.val = pstrdup(string_value);
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index b3f1bc4..a027fbe 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -192,18 +192,6 @@ PerformAuthentication(Port *port)
 	 * FIXME: [fork/exec] Ugh.  Is there a way around this overhead?
 	 */
 #ifdef EXEC_BACKEND
-	/*
-	 * load_hba() and load_ident() want to work within the PostmasterContext,
-	 * so create that if it doesn't exist (which it won't).  We'll delete it
-	 * again later, in PostgresMain.
-	 */
-	if (PostmasterContext == NULL)
-		PostmasterContext = AllocSetContextCreate(TopMemoryContext,
-												  "Postmaster",
-												  ALLOCSET_DEFAULT_MINSIZE,
-												  ALLOCSET_DEFAULT_INITSIZE,
-												  ALLOCSET_DEFAULT_MAXSIZE);
-
 	if (!load_hba())
 	{
 		/*
@@ -739,6 +727,14 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 	}
 
 	/*
+	 * We don't need the HBA and ident data going forward, but we can't rely
+	 * on release of PostmasterContext to clean that up, so discard them
+	 * explicitly here.
+	 */
+	discard_hba();
+	discard_ident();
+
+	/*
 	 * If we're trying to shut down, only superusers can connect, and new
 	 * replication connections are not allowed.
 	 */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index ceb8129..fd32c3e 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3014,6 +3014,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 = 3997 (  pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f f t v u 4 0 2249 "25 25 25 16" "{25,25,25,16,23,25,25,1009,1009,1009,1009,869,869,25,25,3802,25}" "{i,i,i,i,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{database,user_name,address,ssl_inuse,line_number,mode,type,keyword_database,database,keyword_user,user_name,address,netmask,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup _null_ _null_ _null_));
+DESCR("view client authentication settings");
 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 68a953a..291300b 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -98,6 +98,10 @@ typedef struct Port hbaPort;
 
 extern bool load_hba(void);
 extern bool load_ident(void);
+
+extern void discard_hba(void);
+extern void discard_ident(void);
+
 extern void hba_getauthmethod(hbaPort *port);
 extern int check_usermap(const char *usermap_name,
 			  const char *pg_role, const char *auth_user,
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 59a00bb..4f0f31d 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1158,6 +1158,9 @@ extern Datum pg_control_system(PG_FUNCTION_ARGS);
 extern Datum pg_control_init(PG_FUNCTION_ARGS);
 extern Datum pg_control_recovery(PG_FUNCTION_ARGS);
 
+/* hba.c */
+extern Datum pg_hba_lookup(PG_FUNCTION_ARGS);
+
 /* rls.c */
 extern Datum row_security_active(PG_FUNCTION_ARGS);
 extern Datum row_security_active_name(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 5f49d8d..33e00d2 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -427,6 +427,12 @@ extern bool JsonbDeepContains(JsonbIterator **val,
 				  JsonbIterator **mContained);
 extern void JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash);
 
+/* jsonb_util.c support functions */
+void push_jsonb_string_key(JsonbParseState **pstate, char *string_key);
+void push_jsonb_bool_value(JsonbParseState **pstate, bool bool_val);
+void push_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val);
+void push_jsonb_string_value(JsonbParseState **pstate, char *string_value);
+
 /* jsonb.c support functions */
 extern char *JsonbToCString(StringInfo out, JsonbContainer *in,
 			   int estimated_len);
#59Shulgin, Oleksandr
oleksandr.shulgin@zalando.de
In reply to: Haribabu Kommi (#58)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Fri, Mar 18, 2016 at 7:53 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Thu, Mar 17, 2016 at 6:56 PM, Shulgin, Oleksandr
<oleksandr.shulgin@zalando.de> wrote:

You mean change context name and correct the comment? I didn't suggest

to

change the function name.

Changed the context name and the comment only.

Check.

+} lookup_hba_line_context;
^^^^^ but why TAB here?

Still remains an issue of representing special keywords in database

and

user_name fields, but there was no consensus about that though.

How about adding keyword_database and keyword_user columns to listing
out the keywords. These columns will be populated only when the hba

line

contains any keywords.

Hm... that could work too.

Here I attached patch with the added two keyword columns.

+ if (!tok->quoted && strcmp(tok->string, "all") == 0)

token_is_keyword(tok, "all") ?

During the testing with different IP comparison methods like 'samehost' or
'samenet', the address details are not displayed. Is there any need of
showing the IP compare method also?

Do you mean return "samehost/samenet/all" in the yet another
keyword_address out parameter or something like that?

--
Alex

#60Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Shulgin, Oleksandr (#59)
1 attachment(s)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Fri, Mar 18, 2016 at 7:46 PM, Shulgin, Oleksandr
<oleksandr.shulgin@zalando.de> wrote:

On Fri, Mar 18, 2016 at 7:53 AM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Thu, Mar 17, 2016 at 6:56 PM, Shulgin, Oleksandr
<oleksandr.shulgin@zalando.de> wrote:

You mean change context name and correct the comment? I didn't suggest
to
change the function name.

Changed the context name and the comment only.

Check.

+} lookup_hba_line_context;
^^^^^ but why TAB here?

corrected. I am not sure why pg_indent is adding a tab here.

Still remains an issue of representing special keywords in database
and
user_name fields, but there was no consensus about that though.

How about adding keyword_database and keyword_user columns to listing
out the keywords. These columns will be populated only when the hba
line
contains any keywords.

Hm... that could work too.

Here I attached patch with the added two keyword columns.

+ if (!tok->quoted && strcmp(tok->string, "all") == 0)

token_is_keyword(tok, "all") ?

updated.

During the testing with different IP comparison methods like 'samehost' or
'samenet', the address details are not displayed. Is there any need of
showing the IP compare method also?

Do you mean return "samehost/samenet/all" in the yet another keyword_address
out parameter or something like that?

Yes, Currently other than IP/hostname, if the user specifies
samehost/samenet/all
keywords, currently these are not displayed. I feel adding another column of
keyword_address is worth.

Updated patch is attached.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg-hba-lookup-21-03-2016.patchapplication/octet-stream; name=pg-hba-lookup-21-03-2016.patchDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index ae93e69..b286681 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17220,6 +17220,19 @@ SELECT collation for ('foo' COLLATE "de_DE");
        <entry><type>text</type></entry>
        <entry>set parameter and return new value</entry>
       </row>
+      <row>
+       <entry>
+        <indexterm>
+         <primary>pg_hba_lookup</primary>
+        </indexterm>
+        <literal><function>pg_hba_lookup(<parameter>database</> <type>text</>,
+                            <parameter>user_name</> <type>text</>
+                            [, <parameter>address</> <type>text</>]
+                            [, <parameter>ssl_inuse</> <type>text</>)</function></literal>
+       </entry>
+       <entry><type>record</type></entry>
+       <entry>scan <filename>pg_hba.conf</> and return examined entries up to a matching one</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
@@ -17277,6 +17290,32 @@ SELECT set_config('log_statement_stats', 'off', false);
 </programlisting>
    </para>
 
+   <para>
+    <function>pg_hba_lookup</function> returns a set of records containing the
+    line number, mode, type, database, user_name, address, netmask, hostname,
+    method, options and skip reason. For example, to debug problems with user
+    <literal>kommih</> trying to connect to a database <literal>postgres</>
+    from IPv6-address <literal>::1</>, one can issue a following query:
+<programlisting>
+postgres=# select * from pg_hba_lookup('postgres', 'kommih', '::1');
+ line_number |  mode   | type  | keyword_database | database | keyword_user | user_name | keyword_address |  address  |                 netmask                 | hostname | method | options |          reason          
+-------------+---------+-------+------------------+----------+--------------+-----------+-----------------+-----------+-----------------------------------------+----------+--------+---------+--------------------------
+          84 | skipped | local | {all}            |          | {all}        |           |                 |           |                                         |          | trust  | {}      | connection type mismatch
+          86 | skipped | host  | {all}            |          | {all}        |           |                 | 127.0.0.1 | 255.255.255.255                         |          | trust  | {}      | IP address mismatch
+          90 | matched | host  | {all}            |          | {all}        |           |                 | ::1       | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff |          | trust  | {}      | 
+(3 rows)
+
+</programlisting>
+    This function actually loads the contents of <filename>pg_hba.conf</> file
+    into memory to perform matching, thus a database administrator can use it
+    to test the effects of changes made to the file in isolation prior to
+    actually applying the configuration cluster-wide with a signal to the
+    postmaster process.
+
+    Only superusers can access this function to look the
+    <filename>pg_hba.conf</> entries up.
+   </para>
+   
   </sect2>
 
   <sect2 id="functions-admin-signal">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 9ae1ef4..eff567c 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -997,3 +997,13 @@ RETURNS text[]
 LANGUAGE INTERNAL
 STRICT IMMUTABLE
 AS 'parse_ident';
+
+CREATE OR REPLACE FUNCTION pg_hba_lookup(IN database text, IN user_name text,
+    IN address text DEFAULT NULL, IN ssl_inuse boolean DEFAULT false,
+    OUT line_number int, OUT mode text, OUT type text, OUT keyword_database text[],
+    OUT database text[], OUT keyword_user text[], OUT user_name text[],
+    OUT keyword_address text, OUT address inet, OUT netmask inet, OUT hostname text,
+    OUT method text, OUT options jsonb, OUT reason text)
+RETURNS SETOF RECORD
+LANGUAGE INTERNAL
+AS 'pg_hba_lookup';
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 28f9fb5..81ddac9 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -25,15 +25,22 @@
 #include <arpa/inet.h>
 #include <unistd.h>
 
+#include "access/htup_details.h"
+#include "catalog/objectaddress.h"
 #include "catalog/pg_collation.h"
+#include "catalog/pg_type.h"
+#include "funcapi.h"
 #include "libpq/ip.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/jsonb.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 
@@ -52,6 +59,8 @@
 #define MAX_TOKEN	256
 #define MAX_LINE	8192
 
+#define NUM_PG_HBA_LOOKUP_ATTS	 14
+
 /* callback data for check_network_callback */
 typedef struct check_network_data
 {
@@ -75,6 +84,23 @@ typedef struct HbaToken
 } HbaToken;
 
 /*
+ * Optional callback function type for check_hba() function.
+ * Currently, valid callback function is passed to check_hba()
+ * by pg_hba_lookup system function to frame the hba tuple row
+ * based on inputs provided by the user.
+ */
+typedef void (*hba_line_callback) (void *context, HbaLine *hba_line,
+											   const char *reason);
+
+/* Context to use with lookup_hba_line_callback function. */
+typedef struct lookup_hba_line_context
+{
+	MemoryContext memcxt;
+	TupleDesc	tupdesc;
+	Tuplestorestate *tuple_store;
+} lookup_hba_line_context;
+
+/*
  * pre-parsed content of HBA config file: list of HbaLine structs.
  * parsed_hba_context is the memory context where it lives.
  */
@@ -99,6 +125,10 @@ 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);
+static Datum getauthmethod(UserAuth auth_method);
+static Jsonb *gethba_options(HbaLine *hba);
+static void lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason);
+static bool token_is_a_database_keyword(HbaToken *tok);
 
 /*
  * isblank() exists in the ISO C99 spec, but it's not very portable yet,
@@ -541,6 +571,18 @@ check_db(const char *dbname, const char *role, Oid roleid, List *tokens)
 }
 
 static bool
+token_is_a_database_keyword(HbaToken *tok)
+{
+	if (token_is_keyword(tok, "replication")
+		|| token_is_keyword(tok, "all")
+		|| token_is_keyword(tok, "sameuser")
+		|| token_is_keyword(tok, "samegroup")
+		|| token_is_keyword(tok, "samerole"))
+		return true;
+	return false;
+}
+
+static bool
 ipv4eq(struct sockaddr_in * a, struct sockaddr_in * b)
 {
 	return (a->sin_addr.s_addr == b->sin_addr.s_addr);
@@ -1640,7 +1682,8 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  *	request.
  */
 static void
-check_hba(hbaPort *port)
+check_hba(hbaPort *port, hba_line_callback callback,
+		  void *callback_context)
 {
 	Oid			roleid;
 	ListCell   *line;
@@ -1657,25 +1700,41 @@ check_hba(hbaPort *port)
 		if (hba->conntype == ctLocal)
 		{
 			if (!IS_AF_UNIX(port->raddr.addr.ss_family))
+			{
+				if (callback)
+					callback(callback_context, hba, _("connection type mismatch"));
 				continue;
+			}
 		}
 		else
 		{
 			if (IS_AF_UNIX(port->raddr.addr.ss_family))
+			{
+				if (callback)
+					callback(callback_context, hba, _("connection type mismatch"));
 				continue;
+			}
 
 			/* Check SSL state */
 			if (port->ssl_in_use)
 			{
 				/* Connection is SSL, match both "host" and "hostssl" */
 				if (hba->conntype == ctHostNoSSL)
+				{
+					if (callback)
+						callback(callback_context, hba, _("connection type mismatch"));
 					continue;
+				}
 			}
 			else
 			{
 				/* Connection is not SSL, match both "host" and "hostnossl" */
 				if (hba->conntype == ctHostSSL)
+				{
+					if (callback)
+						callback(callback_context, hba, _("connection type mismatch"));
 					continue;
+				}
 			}
 
 			/* Check IP address */
@@ -1686,14 +1745,22 @@ check_hba(hbaPort *port)
 					{
 						if (!check_hostname(port,
 											hba->hostname))
+						{
+							if (callback)
+								callback(callback_context, hba, _("hostname mismatch"));
 							continue;
+						}
 					}
 					else
 					{
 						if (!check_ip(&port->raddr,
 									  (struct sockaddr *) & hba->addr,
 									  (struct sockaddr *) & hba->mask))
+						{
+							if (callback)
+								callback(callback_context, hba, _("IP address mismatch"));
 							continue;
+						}
 					}
 					break;
 				case ipCmpAll:
@@ -1702,7 +1769,11 @@ check_hba(hbaPort *port)
 				case ipCmpSameNet:
 					if (!check_same_host_or_net(&port->raddr,
 												hba->ip_cmp_method))
+					{
+						if (callback)
+							callback(callback_context, hba, _("samehost/samenet mismatch"));
 						continue;
+					}
 					break;
 				default:
 					/* shouldn't get here, but deem it no-match if so */
@@ -1713,10 +1784,21 @@ check_hba(hbaPort *port)
 		/* Check database and role */
 		if (!check_db(port->database_name, port->user_name, roleid,
 					  hba->databases))
+		{
+			if (callback)
+				callback(callback_context, hba, _("database name mismatch"));
 			continue;
+		}
 
 		if (!check_role(port->user_name, roleid, hba->roles))
+		{
+			if (callback)
+				callback(callback_context, hba, _("user name mismatch"));
 			continue;
+		}
+
+		if (callback)
+			callback(callback_context, hba, NULL);
 
 		/* Found a record that matched! */
 		port->hba = hba;
@@ -1770,8 +1852,7 @@ load_hba(void)
 	FreeFile(file);
 
 	/* Now parse all the lines */
-	Assert(PostmasterContext);
-	hbacxt = AllocSetContextCreate(PostmasterContext,
+	hbacxt = AllocSetContextCreate(CurrentMemoryContext,
 								   "hba parser context",
 								   ALLOCSET_DEFAULT_MINSIZE,
 								   ALLOCSET_DEFAULT_MINSIZE,
@@ -1829,14 +1910,26 @@ load_hba(void)
 	}
 
 	/* Loaded new file successfully, replace the one we use */
-	if (parsed_hba_context != NULL)
-		MemoryContextDelete(parsed_hba_context);
+	discard_hba();
 	parsed_hba_context = hbacxt;
 	parsed_hba_lines = new_parsed_lines;
 
 	return true;
 }
 
+
+void
+discard_hba(void)
+{
+	if (parsed_hba_context != NULL)
+	{
+		MemoryContextDelete(parsed_hba_context);
+		parsed_hba_context = NULL;
+		parsed_hba_lines = NIL;
+	}
+}
+
+
 /*
  * Parse one tokenised line from the ident config file and store the result in
  * an IdentLine structure, or NULL if parsing fails.
@@ -2148,8 +2241,7 @@ load_ident(void)
 	FreeFile(file);
 
 	/* Now parse all the lines */
-	Assert(PostmasterContext);
-	ident_context = AllocSetContextCreate(PostmasterContext,
+	ident_context = AllocSetContextCreate(CurrentMemoryContext,
 										  "ident parser context",
 										  ALLOCSET_DEFAULT_MINSIZE,
 										  ALLOCSET_DEFAULT_MINSIZE,
@@ -2202,6 +2294,20 @@ load_ident(void)
 	}
 
 	/* Loaded new file successfully, replace the one we use */
+	discard_ident();
+	parsed_ident_context = ident_context;
+	parsed_ident_lines = new_parsed_lines;
+
+	return true;
+}
+
+
+void
+discard_ident(void)
+{
+	ListCell   *parsed_line_cell;
+	IdentLine  *newline;
+
 	if (parsed_ident_lines != NIL)
 	{
 		foreach(parsed_line_cell, parsed_ident_lines)
@@ -2210,18 +2316,17 @@ load_ident(void)
 			if (newline->ident_user[0] == '/')
 				pg_regfree(&newline->re);
 		}
+		parsed_ident_lines = NIL;
 	}
+
 	if (parsed_ident_context != NULL)
+	{
 		MemoryContextDelete(parsed_ident_context);
-
-	parsed_ident_context = ident_context;
-	parsed_ident_lines = new_parsed_lines;
-
-	return true;
+		parsed_ident_context = NULL;
+	}
 }
 
 
-
 /*
  *	Determine what authentication method should be used when accessing database
  *	"database" from frontend "raddr", user "user".  Return the method and
@@ -2233,5 +2338,567 @@ load_ident(void)
 void
 hba_getauthmethod(hbaPort *port)
 {
-	check_hba(port);
+	check_hba(port, NULL, NULL);
+}
+
+
+/*
+ * 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 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 uaLDAP:
+			result = CStringGetTextDatum("ldap");
+			break;
+		case uaCert:
+			result = CStringGetTextDatum("cert");
+			break;
+		case uaRADIUS:
+			result = CStringGetTextDatum("radius");
+			break;
+		case uaPeer:
+			result = CStringGetTextDatum("peer");
+			break;
+		default:
+			elog(ERROR, "unexpected authentication method in parsed HBA entry");
+			break;
+	}
+
+	return result;
+}
+
+static Jsonb *
+gethba_options(HbaLine *hba)
+{
+	JsonbParseState *parseState = NULL;
+	JsonbValue *result;
+
+	result = pushJsonbValue(&parseState, WJB_BEGIN_OBJECT, NULL);
+
+	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
+	{
+		if (hba->include_realm)
+		{
+			push_jsonb_string_key(&parseState, "include_realm");
+			push_jsonb_bool_value(&parseState, true);
+		}
+
+		if (hba->krb_realm)
+		{
+			push_jsonb_string_key(&parseState, "krb_realm");
+			push_jsonb_string_value(&parseState, hba->krb_realm);
+		}
+	}
+
+	if (hba->usermap)
+	{
+		push_jsonb_string_key(&parseState, "map");
+		push_jsonb_string_value(&parseState, hba->usermap);
+	}
+
+	if (hba->clientcert)
+	{
+		push_jsonb_string_key(&parseState, "clientcert");
+		push_jsonb_bool_value(&parseState, true);
+	}
+
+	if (hba->pamservice)
+	{
+		push_jsonb_string_key(&parseState, "pamservice");
+		push_jsonb_string_value(&parseState, hba->pamservice);
+	}
+
+	if (hba->auth_method == uaLDAP)
+	{
+		if (hba->ldapserver)
+		{
+			push_jsonb_string_key(&parseState, "ldapserver");
+			push_jsonb_string_value(&parseState, hba->ldapserver);
+		}
+
+		if (hba->ldapport)
+		{
+			push_jsonb_string_key(&parseState, "ldapport");
+			push_jsonb_int32_value(&parseState, hba->ldapport);
+		}
+
+		if (hba->ldaptls)
+		{
+			push_jsonb_string_key(&parseState, "ldaptls");
+			push_jsonb_bool_value(&parseState, true);
+		}
+
+		if (hba->ldapprefix)
+		{
+			push_jsonb_string_key(&parseState, "ldapprefix");
+			push_jsonb_string_value(&parseState, hba->ldapprefix);
+		}
+
+		if (hba->ldapsuffix)
+		{
+			push_jsonb_string_key(&parseState, "ldapsuffix");
+			push_jsonb_string_value(&parseState, hba->ldapsuffix);
+		}
+
+		if (hba->ldapbasedn)
+		{
+			push_jsonb_string_key(&parseState, "ldapbasedn");
+			push_jsonb_string_value(&parseState, hba->ldapbasedn);
+		}
+
+		if (hba->ldapbinddn)
+		{
+			push_jsonb_string_key(&parseState, "ldapbinddn");
+			push_jsonb_string_value(&parseState, hba->ldapbinddn);
+		}
+
+		if (hba->ldapbindpasswd)
+		{
+			push_jsonb_string_key(&parseState, "ldapbindpasswd");
+			push_jsonb_string_value(&parseState, hba->ldapbindpasswd);
+		}
+
+		if (hba->ldapsearchattribute)
+		{
+			push_jsonb_string_key(&parseState, "ldapsearchattribute");
+			push_jsonb_string_value(&parseState, hba->ldapsearchattribute);
+		}
+
+		if (hba->ldapscope)
+		{
+			push_jsonb_string_key(&parseState, "ldapscope");
+			push_jsonb_int32_value(&parseState, hba->ldapscope);
+		}
+	}
+
+	if (hba->auth_method == uaRADIUS)
+	{
+		if (hba->radiusserver)
+		{
+			push_jsonb_string_key(&parseState, "radiusserver");
+			push_jsonb_string_value(&parseState, hba->radiusserver);
+		}
+
+		if (hba->radiussecret)
+		{
+			push_jsonb_string_key(&parseState, "radiussecret");
+			push_jsonb_string_value(&parseState, hba->radiussecret);
+		}
+
+		if (hba->radiusidentifier)
+		{
+			push_jsonb_string_key(&parseState, "radiusidentifier");
+			push_jsonb_string_value(&parseState, hba->radiusidentifier);
+		}
+
+		if (hba->radiusport)
+		{
+			push_jsonb_string_key(&parseState, "radiusport");
+			push_jsonb_int32_value(&parseState, hba->radiusport);
+		}
+	}
+
+	result = pushJsonbValue(&parseState, WJB_END_OBJECT, NULL);
+	return JsonbValueToJsonb(result);
+}
+
+
+static void
+lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason)
+{
+	Datum		values[NUM_PG_HBA_LOOKUP_ATTS];
+	bool		nulls[NUM_PG_HBA_LOOKUP_ATTS];
+	ListCell   *dbcell;
+	char		buffer[NI_MAXHOST];
+	HeapTuple	tuple;
+	int			index;
+	MemoryContext old_cxt;
+	lookup_hba_line_context *mycxt;
+
+	mycxt = (lookup_hba_line_context *) context;
+
+	index = 0;
+	memset(values, 0, sizeof(values));
+	memset(nulls, 0, sizeof(nulls));
+
+	old_cxt = MemoryContextSwitchTo(mycxt->memcxt);
+
+	/* line_number */
+	values[index] = Int32GetDatum(hba->linenumber);
+
+	/* mode */
+	index++;
+	if (reason == NULL)
+		values[index] = CStringGetTextDatum(_("matched"));
+	else
+		values[index] = CStringGetTextDatum(_("skipped"));
+
+	/* 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;
+		default:
+			elog(ERROR, "unexpected connection type in parsed HBA entry");
+			break;
+	}
+
+	/* keyword_database and database */
+	index++;
+	if (list_length(hba->databases) != 0)
+	{
+		List	   *names = NULL;
+		List	   *keyword_names = NULL;
+		HbaToken   *tok;
+
+		foreach(dbcell, hba->databases)
+		{
+			tok = lfirst(dbcell);
+			if (token_is_a_database_keyword(tok))
+				keyword_names = lappend(keyword_names, tok->string);
+			else
+				names = lappend(names, tok->string);
+		}
+
+		/* keyword_database */
+		if (keyword_names != NULL)
+			values[index] = PointerGetDatum(strlist_to_textarray(keyword_names));
+		else
+			nulls[index] = true;
+
+		/* database */
+		index++;
+		if (names != NULL)
+			values[index] = PointerGetDatum(strlist_to_textarray(names));
+		else
+			nulls[index] = true;
+	}
+	else
+	{
+		nulls[index] = true;
+		index++;
+		nulls[index] = true;
+	}
+
+	/* keyword_user and user */
+	index++;
+	if (list_length(hba->roles) != 0)
+	{
+		List	   *roles = NULL;
+		List	   *keyword_roles = NULL;
+		HbaToken   *tok;
+
+		foreach(dbcell, hba->roles)
+		{
+			tok = lfirst(dbcell);
+			if (token_is_keyword(tok, "all"))
+				keyword_roles = lappend(keyword_roles, tok->string);
+			else
+				roles = lappend(roles, tok->string);
+		}
+
+		/* keyword_user */
+		if (keyword_roles != NULL)
+			values[index] = PointerGetDatum(strlist_to_textarray(keyword_roles));
+		else
+			nulls[index] = true;
+
+		/* user */
+		index++;
+		if (roles != NULL)
+			values[index] = PointerGetDatum(strlist_to_textarray(roles));
+		else
+			nulls[index] = true;
+	}
+	else
+	{
+		nulls[index] = true;
+		index++;
+		nulls[index] = true;
+	}
+
+	/* keyword_address */
+	index++;
+	switch (hba->ip_cmp_method)
+	{
+		case ipCmpAll:
+			values[index] = CStringGetTextDatum("all");
+			break;
+		case ipCmpSameHost:
+			values[index] = CStringGetTextDatum("samehost");
+			break;
+		case ipCmpSameNet:
+			values[index] = CStringGetTextDatum("samenet");
+			break;
+		default:
+			nulls[index] = true;
+			break;
+	}
+
+	/* address */
+	index++;
+	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+	}
+	else
+		nulls[index] = true;
+
+	/* netmask */
+	index++;
+	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+	}
+	else
+		nulls[index] = true;
+
+	/* hostname */
+	index++;
+	if (hba->hostname)
+		values[index] = CStringGetTextDatum(hba->hostname);
+	else
+		nulls[index] = true;
+
+	/* method */
+	index++;
+	values[index] = getauthmethod(hba->auth_method);
+
+	/* options */
+	index++;
+	values[index] = PointerGetDatum(gethba_options(hba));
+
+	/* reason */
+	index++;
+	if (reason)
+		values[index] = CStringGetTextDatum(reason);
+	else
+		nulls[index] = true;
+
+	tuple = heap_form_tuple(mycxt->tupdesc, values, nulls);
+	tuplestore_puttuple(mycxt->tuple_store, tuple);
+
+	MemoryContextSwitchTo(old_cxt);
+	return;
+}
+
+/*
+ * SQL-accessible SRF to return all the settings from the pg_hba.conf
+ * file.
+ */
+Datum
+pg_hba_lookup(PG_FUNCTION_ARGS)
+{
+	hbaPort    *port;
+	Tuplestorestate *tuple_store;
+	TupleDesc	tupdesc;
+	MemoryContext old_cxt;
+	lookup_hba_line_context *mycxt;
+	ReturnSetInfo *rsi;
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to view pg_hba.conf settings"))));
+
+	/*
+	 * 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;
+
+	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+		(rsi->allowedModes & SFRM_Materialize) == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+
+	port = palloc0(sizeof(hbaPort));
+
+	if (PG_ARGISNULL(0))
+		ereport(ERROR,
+				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				 (errmsg("database name is required to match pg_hba configuration entry"))));
+	else
+		port->database_name = TextDatumGetCString(PG_GETARG_DATUM(0));
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				 (errmsg("user name is required to match pg_hba configuration entry"))));
+	else
+		port->user_name = TextDatumGetCString(PG_GETARG_DATUM(1));
+
+
+	if (!PG_ARGISNULL(2))
+	{
+		char	   *address = NULL;
+		struct addrinfo *gai_result = NULL;
+		struct addrinfo hints;
+		int			ret;
+
+		address = TextDatumGetCString(PG_GETARG_DATUM(2));
+
+		/* Get the IP address either way */
+		hints.ai_flags = AI_NUMERICHOST;
+		hints.ai_family = AF_UNSPEC;
+		hints.ai_socktype = 0;
+		hints.ai_protocol = 0;
+		hints.ai_addrlen = 0;
+		hints.ai_canonname = NULL;
+		hints.ai_addr = NULL;
+		hints.ai_next = NULL;
+
+		ret = getaddrinfo(address, NULL, &hints, &gai_result);
+		if (ret == 0 && gai_result)
+			memcpy(&port->raddr.addr, gai_result->ai_addr, gai_result->ai_addrlen);
+		else if (ret == EAI_NONAME)
+		{
+			struct addrinfo *gai_result2 = NULL;
+
+			port->remote_hostname = pstrdup(address);
+
+			ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result2);
+			if (ret == 0 && gai_result2)
+				memcpy(&port->raddr.addr, gai_result2->ai_addr, gai_result2->ai_addrlen);
+			else
+				ereport(ERROR,
+				(errmsg("getaddrinfo failed to look into hostname \"%s\": %s",
+						port->remote_hostname, gai_strerror(ret))));
+
+			if (gai_result2)
+				freeaddrinfo(gai_result2);
+		}
+		else
+			ereport(ERROR,
+					(errmsg("invalid IP address \"%s\": %s",
+							address, gai_strerror(ret))));
+		if (gai_result)
+			freeaddrinfo(gai_result);
+	}
+	else
+		port->raddr.addr.ss_family = AF_UNIX;
+
+	port->ssl_in_use = DatumGetBool(PG_GETARG_DATUM(3));
+
+	rsi->returnMode = SFRM_Materialize;
+
+	if (!load_hba())
+		ereport(ERROR,
+				(errmsg("failed to load pg_hba.conf file"),
+			   errhint("more details may be available in the server log.")));
+
+	/*
+	 * Create the tupledesc and tuplestore in the per_query context as
+	 * required for SFRM_Materialize.
+	 */
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+
+	tupdesc = CreateTemplateTupleDesc(NUM_PG_HBA_LOOKUP_ATTS, false);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "line_number",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "mode",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "type",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "keyword_database",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "database",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "keyword_user",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "user_name",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "keyword_address",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "address",
+					   INETOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "netmask",
+					   INETOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "hostname",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 12, "method",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 13, "options",
+					   JSONBOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 14, "reason",
+					   TEXTOID, -1, 0);
+	BlessTupleDesc(tupdesc);
+
+	tuple_store =
+		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+							  false, work_mem);
+
+	MemoryContextSwitchTo(old_cxt);
+
+	mycxt = (lookup_hba_line_context *) palloc(sizeof(lookup_hba_line_context));
+	mycxt->memcxt = AllocSetContextCreate(CurrentMemoryContext,
+										  "pg_hba_lookup tuple cxt",
+										  ALLOCSET_DEFAULT_MINSIZE,
+										  ALLOCSET_DEFAULT_INITSIZE,
+										  ALLOCSET_DEFAULT_MAXSIZE);
+	mycxt->tupdesc = tupdesc;
+	mycxt->tuple_store = tuple_store;
+
+	check_hba(port, lookup_hba_line_callback, mycxt);
+
+	MemoryContextDelete(mycxt->memcxt);
+	pfree(mycxt);
+
+	/* Clean up the memory allocated by load_hba() explicitly. */
+	discard_hba();
+
+	rsi->setDesc = tupdesc;
+	rsi->setResult = tuple_store;
+
+	PG_RETURN_NULL();
 }
diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c
index e1ceea6..171e532 100644
--- a/src/backend/utils/adt/jsonb_util.c
+++ b/src/backend/utils/adt/jsonb_util.c
@@ -1802,3 +1802,49 @@ uniqueifyJsonbObject(JsonbValue *object)
 		object->val.object.nPairs = res + 1 - object->val.object.pairs;
 	}
 }
+
+void
+push_jsonb_string_key(JsonbParseState **pstate, char *string_key)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvString;
+	jb.val.string.len = strlen(string_key);
+	jb.val.string.val = pstrdup(string_key);
+	pushJsonbValue(pstate, WJB_KEY, &jb);
+}
+
+void
+push_jsonb_bool_value(JsonbParseState **pstate, bool bool_val)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvBool;
+	jb.val.boolean = bool_val;
+
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
+
+void
+push_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val)
+{
+	JsonbValue	jb;
+	char		outputstr[64];
+
+	snprintf(outputstr, 64, "%d", int32_val);
+	jb.type = jbvNumeric;
+	jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1));
+
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
+
+void
+push_jsonb_string_value(JsonbParseState **pstate, char *string_value)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvString;
+	jb.val.string.len = strlen(string_value);
+	jb.val.string.val = pstrdup(string_value);
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index b3f1bc4..a027fbe 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -192,18 +192,6 @@ PerformAuthentication(Port *port)
 	 * FIXME: [fork/exec] Ugh.  Is there a way around this overhead?
 	 */
 #ifdef EXEC_BACKEND
-	/*
-	 * load_hba() and load_ident() want to work within the PostmasterContext,
-	 * so create that if it doesn't exist (which it won't).  We'll delete it
-	 * again later, in PostgresMain.
-	 */
-	if (PostmasterContext == NULL)
-		PostmasterContext = AllocSetContextCreate(TopMemoryContext,
-												  "Postmaster",
-												  ALLOCSET_DEFAULT_MINSIZE,
-												  ALLOCSET_DEFAULT_INITSIZE,
-												  ALLOCSET_DEFAULT_MAXSIZE);
-
 	if (!load_hba())
 	{
 		/*
@@ -739,6 +727,14 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 	}
 
 	/*
+	 * We don't need the HBA and ident data going forward, but we can't rely
+	 * on release of PostmasterContext to clean that up, so discard them
+	 * explicitly here.
+	 */
+	discard_hba();
+	discard_ident();
+
+	/*
 	 * If we're trying to shut down, only superusers can connect, and new
 	 * replication connections are not allowed.
 	 */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index a595327..16f3f4a 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3014,6 +3014,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 = 3997 (  pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f f t v u 4 0 2249 "25 25 25 16" "{25,25,25,16,23,25,25,1009,1009,1009,1009,25,869,869,25,25,3802,25}" "{i,i,i,i,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{database,user_name,address,ssl_inuse,line_number,mode,type,keyword_database,database,keyword_user,user_name,keyword_address,address,netmask,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup _null_ _null_ _null_));
+DESCR("view client authentication settings");
 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 68a953a..291300b 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -98,6 +98,10 @@ typedef struct Port hbaPort;
 
 extern bool load_hba(void);
 extern bool load_ident(void);
+
+extern void discard_hba(void);
+extern void discard_ident(void);
+
 extern void hba_getauthmethod(hbaPort *port);
 extern int check_usermap(const char *usermap_name,
 			  const char *pg_role, const char *auth_user,
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 206288d..96e0564 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1159,6 +1159,9 @@ extern Datum pg_control_system(PG_FUNCTION_ARGS);
 extern Datum pg_control_init(PG_FUNCTION_ARGS);
 extern Datum pg_control_recovery(PG_FUNCTION_ARGS);
 
+/* hba.c */
+extern Datum pg_hba_lookup(PG_FUNCTION_ARGS);
+
 /* rls.c */
 extern Datum row_security_active(PG_FUNCTION_ARGS);
 extern Datum row_security_active_name(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 5f49d8d..33e00d2 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -427,6 +427,12 @@ extern bool JsonbDeepContains(JsonbIterator **val,
 				  JsonbIterator **mContained);
 extern void JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash);
 
+/* jsonb_util.c support functions */
+void push_jsonb_string_key(JsonbParseState **pstate, char *string_key);
+void push_jsonb_bool_value(JsonbParseState **pstate, bool bool_val);
+void push_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val);
+void push_jsonb_string_value(JsonbParseState **pstate, char *string_value);
+
 /* jsonb.c support functions */
 extern char *JsonbToCString(StringInfo out, JsonbContainer *in,
 			   int estimated_len);
#61Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Haribabu Kommi (#60)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

Haribabu Kommi wrote:

Check.

+} lookup_hba_line_context;
^^^^^ but why TAB here?

corrected. I am not sure why pg_indent is adding a tab here.

It's because lookup_hba_line_context is not listed in typedefs.list.
I suggest adding it and all other new typedefs you add, and rerunning
pgindent, as the lack of those may affect other places where those names
appear.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#62Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Alvaro Herrera (#61)
1 attachment(s)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Mon, Mar 21, 2016 at 2:00 PM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

Haribabu Kommi wrote:

Check.

+} lookup_hba_line_context;
^^^^^ but why TAB here?

corrected. I am not sure why pg_indent is adding a tab here.

It's because lookup_hba_line_context is not listed in typedefs.list.
I suggest adding it and all other new typedefs you add, and rerunning
pgindent, as the lack of those may affect other places where those names
appear.

Thanks for the details. I added the new typedef into typedefs.list file.
Updated patch is attached.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

pg-hba-lookup-21-03-2016_1.patchapplication/octet-stream; name=pg-hba-lookup-21-03-2016_1.patchDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index ae93e69..b286681 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17220,6 +17220,19 @@ SELECT collation for ('foo' COLLATE "de_DE");
        <entry><type>text</type></entry>
        <entry>set parameter and return new value</entry>
       </row>
+      <row>
+       <entry>
+        <indexterm>
+         <primary>pg_hba_lookup</primary>
+        </indexterm>
+        <literal><function>pg_hba_lookup(<parameter>database</> <type>text</>,
+                            <parameter>user_name</> <type>text</>
+                            [, <parameter>address</> <type>text</>]
+                            [, <parameter>ssl_inuse</> <type>text</>)</function></literal>
+       </entry>
+       <entry><type>record</type></entry>
+       <entry>scan <filename>pg_hba.conf</> and return examined entries up to a matching one</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
@@ -17277,6 +17290,32 @@ SELECT set_config('log_statement_stats', 'off', false);
 </programlisting>
    </para>
 
+   <para>
+    <function>pg_hba_lookup</function> returns a set of records containing the
+    line number, mode, type, database, user_name, address, netmask, hostname,
+    method, options and skip reason. For example, to debug problems with user
+    <literal>kommih</> trying to connect to a database <literal>postgres</>
+    from IPv6-address <literal>::1</>, one can issue a following query:
+<programlisting>
+postgres=# select * from pg_hba_lookup('postgres', 'kommih', '::1');
+ line_number |  mode   | type  | keyword_database | database | keyword_user | user_name | keyword_address |  address  |                 netmask                 | hostname | method | options |          reason          
+-------------+---------+-------+------------------+----------+--------------+-----------+-----------------+-----------+-----------------------------------------+----------+--------+---------+--------------------------
+          84 | skipped | local | {all}            |          | {all}        |           |                 |           |                                         |          | trust  | {}      | connection type mismatch
+          86 | skipped | host  | {all}            |          | {all}        |           |                 | 127.0.0.1 | 255.255.255.255                         |          | trust  | {}      | IP address mismatch
+          90 | matched | host  | {all}            |          | {all}        |           |                 | ::1       | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff |          | trust  | {}      | 
+(3 rows)
+
+</programlisting>
+    This function actually loads the contents of <filename>pg_hba.conf</> file
+    into memory to perform matching, thus a database administrator can use it
+    to test the effects of changes made to the file in isolation prior to
+    actually applying the configuration cluster-wide with a signal to the
+    postmaster process.
+
+    Only superusers can access this function to look the
+    <filename>pg_hba.conf</> entries up.
+   </para>
+   
   </sect2>
 
   <sect2 id="functions-admin-signal">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 9ae1ef4..eff567c 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -997,3 +997,13 @@ RETURNS text[]
 LANGUAGE INTERNAL
 STRICT IMMUTABLE
 AS 'parse_ident';
+
+CREATE OR REPLACE FUNCTION pg_hba_lookup(IN database text, IN user_name text,
+    IN address text DEFAULT NULL, IN ssl_inuse boolean DEFAULT false,
+    OUT line_number int, OUT mode text, OUT type text, OUT keyword_database text[],
+    OUT database text[], OUT keyword_user text[], OUT user_name text[],
+    OUT keyword_address text, OUT address inet, OUT netmask inet, OUT hostname text,
+    OUT method text, OUT options jsonb, OUT reason text)
+RETURNS SETOF RECORD
+LANGUAGE INTERNAL
+AS 'pg_hba_lookup';
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 28f9fb5..27e8c0c 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -25,15 +25,22 @@
 #include <arpa/inet.h>
 #include <unistd.h>
 
+#include "access/htup_details.h"
+#include "catalog/objectaddress.h"
 #include "catalog/pg_collation.h"
+#include "catalog/pg_type.h"
+#include "funcapi.h"
 #include "libpq/ip.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/jsonb.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 
@@ -52,6 +59,8 @@
 #define MAX_TOKEN	256
 #define MAX_LINE	8192
 
+#define NUM_PG_HBA_LOOKUP_ATTS	 14
+
 /* callback data for check_network_callback */
 typedef struct check_network_data
 {
@@ -75,6 +84,23 @@ typedef struct HbaToken
 } HbaToken;
 
 /*
+ * Optional callback function type for check_hba() function.
+ * Currently, valid callback function is passed to check_hba()
+ * by pg_hba_lookup system function to frame the hba tuple row
+ * based on inputs provided by the user.
+ */
+typedef void (*hba_line_callback) (void *context, HbaLine *hba_line,
+											   const char *reason);
+
+/* Context to use with lookup_hba_line_callback function. */
+typedef struct LookupHbaLineCxt
+{
+	MemoryContext memcxt;
+	TupleDesc	tupdesc;
+	Tuplestorestate *tuple_store;
+} LookupHbaLineCxt;
+
+/*
  * pre-parsed content of HBA config file: list of HbaLine structs.
  * parsed_hba_context is the memory context where it lives.
  */
@@ -99,6 +125,10 @@ 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);
+static Datum getauthmethod(UserAuth auth_method);
+static Jsonb *gethba_options(HbaLine *hba);
+static void lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason);
+static bool token_is_a_database_keyword(HbaToken *tok);
 
 /*
  * isblank() exists in the ISO C99 spec, but it's not very portable yet,
@@ -541,6 +571,18 @@ check_db(const char *dbname, const char *role, Oid roleid, List *tokens)
 }
 
 static bool
+token_is_a_database_keyword(HbaToken *tok)
+{
+	if (token_is_keyword(tok, "replication")
+		|| token_is_keyword(tok, "all")
+		|| token_is_keyword(tok, "sameuser")
+		|| token_is_keyword(tok, "samegroup")
+		|| token_is_keyword(tok, "samerole"))
+		return true;
+	return false;
+}
+
+static bool
 ipv4eq(struct sockaddr_in * a, struct sockaddr_in * b)
 {
 	return (a->sin_addr.s_addr == b->sin_addr.s_addr);
@@ -1640,7 +1682,8 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  *	request.
  */
 static void
-check_hba(hbaPort *port)
+check_hba(hbaPort *port, hba_line_callback callback,
+		  void *callback_context)
 {
 	Oid			roleid;
 	ListCell   *line;
@@ -1657,25 +1700,41 @@ check_hba(hbaPort *port)
 		if (hba->conntype == ctLocal)
 		{
 			if (!IS_AF_UNIX(port->raddr.addr.ss_family))
+			{
+				if (callback)
+					callback(callback_context, hba, _("connection type mismatch"));
 				continue;
+			}
 		}
 		else
 		{
 			if (IS_AF_UNIX(port->raddr.addr.ss_family))
+			{
+				if (callback)
+					callback(callback_context, hba, _("connection type mismatch"));
 				continue;
+			}
 
 			/* Check SSL state */
 			if (port->ssl_in_use)
 			{
 				/* Connection is SSL, match both "host" and "hostssl" */
 				if (hba->conntype == ctHostNoSSL)
+				{
+					if (callback)
+						callback(callback_context, hba, _("connection type mismatch"));
 					continue;
+				}
 			}
 			else
 			{
 				/* Connection is not SSL, match both "host" and "hostnossl" */
 				if (hba->conntype == ctHostSSL)
+				{
+					if (callback)
+						callback(callback_context, hba, _("connection type mismatch"));
 					continue;
+				}
 			}
 
 			/* Check IP address */
@@ -1686,14 +1745,22 @@ check_hba(hbaPort *port)
 					{
 						if (!check_hostname(port,
 											hba->hostname))
+						{
+							if (callback)
+								callback(callback_context, hba, _("hostname mismatch"));
 							continue;
+						}
 					}
 					else
 					{
 						if (!check_ip(&port->raddr,
 									  (struct sockaddr *) & hba->addr,
 									  (struct sockaddr *) & hba->mask))
+						{
+							if (callback)
+								callback(callback_context, hba, _("IP address mismatch"));
 							continue;
+						}
 					}
 					break;
 				case ipCmpAll:
@@ -1702,7 +1769,11 @@ check_hba(hbaPort *port)
 				case ipCmpSameNet:
 					if (!check_same_host_or_net(&port->raddr,
 												hba->ip_cmp_method))
+					{
+						if (callback)
+							callback(callback_context, hba, _("samehost/samenet mismatch"));
 						continue;
+					}
 					break;
 				default:
 					/* shouldn't get here, but deem it no-match if so */
@@ -1713,10 +1784,21 @@ check_hba(hbaPort *port)
 		/* Check database and role */
 		if (!check_db(port->database_name, port->user_name, roleid,
 					  hba->databases))
+		{
+			if (callback)
+				callback(callback_context, hba, _("database name mismatch"));
 			continue;
+		}
 
 		if (!check_role(port->user_name, roleid, hba->roles))
+		{
+			if (callback)
+				callback(callback_context, hba, _("user name mismatch"));
 			continue;
+		}
+
+		if (callback)
+			callback(callback_context, hba, NULL);
 
 		/* Found a record that matched! */
 		port->hba = hba;
@@ -1770,8 +1852,7 @@ load_hba(void)
 	FreeFile(file);
 
 	/* Now parse all the lines */
-	Assert(PostmasterContext);
-	hbacxt = AllocSetContextCreate(PostmasterContext,
+	hbacxt = AllocSetContextCreate(CurrentMemoryContext,
 								   "hba parser context",
 								   ALLOCSET_DEFAULT_MINSIZE,
 								   ALLOCSET_DEFAULT_MINSIZE,
@@ -1829,14 +1910,26 @@ load_hba(void)
 	}
 
 	/* Loaded new file successfully, replace the one we use */
-	if (parsed_hba_context != NULL)
-		MemoryContextDelete(parsed_hba_context);
+	discard_hba();
 	parsed_hba_context = hbacxt;
 	parsed_hba_lines = new_parsed_lines;
 
 	return true;
 }
 
+
+void
+discard_hba(void)
+{
+	if (parsed_hba_context != NULL)
+	{
+		MemoryContextDelete(parsed_hba_context);
+		parsed_hba_context = NULL;
+		parsed_hba_lines = NIL;
+	}
+}
+
+
 /*
  * Parse one tokenised line from the ident config file and store the result in
  * an IdentLine structure, or NULL if parsing fails.
@@ -2148,8 +2241,7 @@ load_ident(void)
 	FreeFile(file);
 
 	/* Now parse all the lines */
-	Assert(PostmasterContext);
-	ident_context = AllocSetContextCreate(PostmasterContext,
+	ident_context = AllocSetContextCreate(CurrentMemoryContext,
 										  "ident parser context",
 										  ALLOCSET_DEFAULT_MINSIZE,
 										  ALLOCSET_DEFAULT_MINSIZE,
@@ -2202,6 +2294,20 @@ load_ident(void)
 	}
 
 	/* Loaded new file successfully, replace the one we use */
+	discard_ident();
+	parsed_ident_context = ident_context;
+	parsed_ident_lines = new_parsed_lines;
+
+	return true;
+}
+
+
+void
+discard_ident(void)
+{
+	ListCell   *parsed_line_cell;
+	IdentLine  *newline;
+
 	if (parsed_ident_lines != NIL)
 	{
 		foreach(parsed_line_cell, parsed_ident_lines)
@@ -2210,18 +2316,17 @@ load_ident(void)
 			if (newline->ident_user[0] == '/')
 				pg_regfree(&newline->re);
 		}
+		parsed_ident_lines = NIL;
 	}
+
 	if (parsed_ident_context != NULL)
+	{
 		MemoryContextDelete(parsed_ident_context);
-
-	parsed_ident_context = ident_context;
-	parsed_ident_lines = new_parsed_lines;
-
-	return true;
+		parsed_ident_context = NULL;
+	}
 }
 
 
-
 /*
  *	Determine what authentication method should be used when accessing database
  *	"database" from frontend "raddr", user "user".  Return the method and
@@ -2233,5 +2338,567 @@ load_ident(void)
 void
 hba_getauthmethod(hbaPort *port)
 {
-	check_hba(port);
+	check_hba(port, NULL, NULL);
+}
+
+
+/*
+ * 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 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 uaLDAP:
+			result = CStringGetTextDatum("ldap");
+			break;
+		case uaCert:
+			result = CStringGetTextDatum("cert");
+			break;
+		case uaRADIUS:
+			result = CStringGetTextDatum("radius");
+			break;
+		case uaPeer:
+			result = CStringGetTextDatum("peer");
+			break;
+		default:
+			elog(ERROR, "unexpected authentication method in parsed HBA entry");
+			break;
+	}
+
+	return result;
+}
+
+static Jsonb *
+gethba_options(HbaLine *hba)
+{
+	JsonbParseState *parseState = NULL;
+	JsonbValue *result;
+
+	result = pushJsonbValue(&parseState, WJB_BEGIN_OBJECT, NULL);
+
+	if (hba->auth_method == uaGSS || hba->auth_method == uaSSPI)
+	{
+		if (hba->include_realm)
+		{
+			push_jsonb_string_key(&parseState, "include_realm");
+			push_jsonb_bool_value(&parseState, true);
+		}
+
+		if (hba->krb_realm)
+		{
+			push_jsonb_string_key(&parseState, "krb_realm");
+			push_jsonb_string_value(&parseState, hba->krb_realm);
+		}
+	}
+
+	if (hba->usermap)
+	{
+		push_jsonb_string_key(&parseState, "map");
+		push_jsonb_string_value(&parseState, hba->usermap);
+	}
+
+	if (hba->clientcert)
+	{
+		push_jsonb_string_key(&parseState, "clientcert");
+		push_jsonb_bool_value(&parseState, true);
+	}
+
+	if (hba->pamservice)
+	{
+		push_jsonb_string_key(&parseState, "pamservice");
+		push_jsonb_string_value(&parseState, hba->pamservice);
+	}
+
+	if (hba->auth_method == uaLDAP)
+	{
+		if (hba->ldapserver)
+		{
+			push_jsonb_string_key(&parseState, "ldapserver");
+			push_jsonb_string_value(&parseState, hba->ldapserver);
+		}
+
+		if (hba->ldapport)
+		{
+			push_jsonb_string_key(&parseState, "ldapport");
+			push_jsonb_int32_value(&parseState, hba->ldapport);
+		}
+
+		if (hba->ldaptls)
+		{
+			push_jsonb_string_key(&parseState, "ldaptls");
+			push_jsonb_bool_value(&parseState, true);
+		}
+
+		if (hba->ldapprefix)
+		{
+			push_jsonb_string_key(&parseState, "ldapprefix");
+			push_jsonb_string_value(&parseState, hba->ldapprefix);
+		}
+
+		if (hba->ldapsuffix)
+		{
+			push_jsonb_string_key(&parseState, "ldapsuffix");
+			push_jsonb_string_value(&parseState, hba->ldapsuffix);
+		}
+
+		if (hba->ldapbasedn)
+		{
+			push_jsonb_string_key(&parseState, "ldapbasedn");
+			push_jsonb_string_value(&parseState, hba->ldapbasedn);
+		}
+
+		if (hba->ldapbinddn)
+		{
+			push_jsonb_string_key(&parseState, "ldapbinddn");
+			push_jsonb_string_value(&parseState, hba->ldapbinddn);
+		}
+
+		if (hba->ldapbindpasswd)
+		{
+			push_jsonb_string_key(&parseState, "ldapbindpasswd");
+			push_jsonb_string_value(&parseState, hba->ldapbindpasswd);
+		}
+
+		if (hba->ldapsearchattribute)
+		{
+			push_jsonb_string_key(&parseState, "ldapsearchattribute");
+			push_jsonb_string_value(&parseState, hba->ldapsearchattribute);
+		}
+
+		if (hba->ldapscope)
+		{
+			push_jsonb_string_key(&parseState, "ldapscope");
+			push_jsonb_int32_value(&parseState, hba->ldapscope);
+		}
+	}
+
+	if (hba->auth_method == uaRADIUS)
+	{
+		if (hba->radiusserver)
+		{
+			push_jsonb_string_key(&parseState, "radiusserver");
+			push_jsonb_string_value(&parseState, hba->radiusserver);
+		}
+
+		if (hba->radiussecret)
+		{
+			push_jsonb_string_key(&parseState, "radiussecret");
+			push_jsonb_string_value(&parseState, hba->radiussecret);
+		}
+
+		if (hba->radiusidentifier)
+		{
+			push_jsonb_string_key(&parseState, "radiusidentifier");
+			push_jsonb_string_value(&parseState, hba->radiusidentifier);
+		}
+
+		if (hba->radiusport)
+		{
+			push_jsonb_string_key(&parseState, "radiusport");
+			push_jsonb_int32_value(&parseState, hba->radiusport);
+		}
+	}
+
+	result = pushJsonbValue(&parseState, WJB_END_OBJECT, NULL);
+	return JsonbValueToJsonb(result);
+}
+
+
+static void
+lookup_hba_line_callback(void *context, HbaLine *hba, const char *reason)
+{
+	Datum		values[NUM_PG_HBA_LOOKUP_ATTS];
+	bool		nulls[NUM_PG_HBA_LOOKUP_ATTS];
+	ListCell   *dbcell;
+	char		buffer[NI_MAXHOST];
+	HeapTuple	tuple;
+	int			index;
+	MemoryContext old_cxt;
+	LookupHbaLineCxt *mycxt;
+
+	mycxt = (LookupHbaLineCxt *) context;
+
+	index = 0;
+	memset(values, 0, sizeof(values));
+	memset(nulls, 0, sizeof(nulls));
+
+	old_cxt = MemoryContextSwitchTo(mycxt->memcxt);
+
+	/* line_number */
+	values[index] = Int32GetDatum(hba->linenumber);
+
+	/* mode */
+	index++;
+	if (reason == NULL)
+		values[index] = CStringGetTextDatum(_("matched"));
+	else
+		values[index] = CStringGetTextDatum(_("skipped"));
+
+	/* 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;
+		default:
+			elog(ERROR, "unexpected connection type in parsed HBA entry");
+			break;
+	}
+
+	/* keyword_database and database */
+	index++;
+	if (list_length(hba->databases) != 0)
+	{
+		List	   *names = NULL;
+		List	   *keyword_names = NULL;
+		HbaToken   *tok;
+
+		foreach(dbcell, hba->databases)
+		{
+			tok = lfirst(dbcell);
+			if (token_is_a_database_keyword(tok))
+				keyword_names = lappend(keyword_names, tok->string);
+			else
+				names = lappend(names, tok->string);
+		}
+
+		/* keyword_database */
+		if (keyword_names != NULL)
+			values[index] = PointerGetDatum(strlist_to_textarray(keyword_names));
+		else
+			nulls[index] = true;
+
+		/* database */
+		index++;
+		if (names != NULL)
+			values[index] = PointerGetDatum(strlist_to_textarray(names));
+		else
+			nulls[index] = true;
+	}
+	else
+	{
+		nulls[index] = true;
+		index++;
+		nulls[index] = true;
+	}
+
+	/* keyword_user and user */
+	index++;
+	if (list_length(hba->roles) != 0)
+	{
+		List	   *roles = NULL;
+		List	   *keyword_roles = NULL;
+		HbaToken   *tok;
+
+		foreach(dbcell, hba->roles)
+		{
+			tok = lfirst(dbcell);
+			if (token_is_keyword(tok, "all"))
+				keyword_roles = lappend(keyword_roles, tok->string);
+			else
+				roles = lappend(roles, tok->string);
+		}
+
+		/* keyword_user */
+		if (keyword_roles != NULL)
+			values[index] = PointerGetDatum(strlist_to_textarray(keyword_roles));
+		else
+			nulls[index] = true;
+
+		/* user */
+		index++;
+		if (roles != NULL)
+			values[index] = PointerGetDatum(strlist_to_textarray(roles));
+		else
+			nulls[index] = true;
+	}
+	else
+	{
+		nulls[index] = true;
+		index++;
+		nulls[index] = true;
+	}
+
+	/* keyword_address */
+	index++;
+	switch (hba->ip_cmp_method)
+	{
+		case ipCmpAll:
+			values[index] = CStringGetTextDatum("all");
+			break;
+		case ipCmpSameHost:
+			values[index] = CStringGetTextDatum("samehost");
+			break;
+		case ipCmpSameNet:
+			values[index] = CStringGetTextDatum("samenet");
+			break;
+		default:
+			nulls[index] = true;
+			break;
+	}
+
+	/* address */
+	index++;
+	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+	}
+	else
+		nulls[index] = true;
+
+	/* netmask */
+	index++;
+	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] = DirectFunctionCall1(inet_in, CStringGetDatum(buffer));
+	}
+	else
+		nulls[index] = true;
+
+	/* hostname */
+	index++;
+	if (hba->hostname)
+		values[index] = CStringGetTextDatum(hba->hostname);
+	else
+		nulls[index] = true;
+
+	/* method */
+	index++;
+	values[index] = getauthmethod(hba->auth_method);
+
+	/* options */
+	index++;
+	values[index] = PointerGetDatum(gethba_options(hba));
+
+	/* reason */
+	index++;
+	if (reason)
+		values[index] = CStringGetTextDatum(reason);
+	else
+		nulls[index] = true;
+
+	tuple = heap_form_tuple(mycxt->tupdesc, values, nulls);
+	tuplestore_puttuple(mycxt->tuple_store, tuple);
+
+	MemoryContextSwitchTo(old_cxt);
+	return;
+}
+
+/*
+ * SQL-accessible SRF to return all the settings from the pg_hba.conf
+ * file.
+ */
+Datum
+pg_hba_lookup(PG_FUNCTION_ARGS)
+{
+	hbaPort    *port;
+	Tuplestorestate *tuple_store;
+	TupleDesc	tupdesc;
+	MemoryContext old_cxt;
+	LookupHbaLineCxt *mycxt;
+	ReturnSetInfo *rsi;
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 (errmsg("must be superuser to view pg_hba.conf settings"))));
+
+	/*
+	 * 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;
+
+	if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+		(rsi->allowedModes & SFRM_Materialize) == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+
+	port = palloc0(sizeof(hbaPort));
+
+	if (PG_ARGISNULL(0))
+		ereport(ERROR,
+				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				 (errmsg("database name is required to match pg_hba configuration entry"))));
+	else
+		port->database_name = TextDatumGetCString(PG_GETARG_DATUM(0));
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				 (errmsg("user name is required to match pg_hba configuration entry"))));
+	else
+		port->user_name = TextDatumGetCString(PG_GETARG_DATUM(1));
+
+
+	if (!PG_ARGISNULL(2))
+	{
+		char	   *address = NULL;
+		struct addrinfo *gai_result = NULL;
+		struct addrinfo hints;
+		int			ret;
+
+		address = TextDatumGetCString(PG_GETARG_DATUM(2));
+
+		/* Get the IP address either way */
+		hints.ai_flags = AI_NUMERICHOST;
+		hints.ai_family = AF_UNSPEC;
+		hints.ai_socktype = 0;
+		hints.ai_protocol = 0;
+		hints.ai_addrlen = 0;
+		hints.ai_canonname = NULL;
+		hints.ai_addr = NULL;
+		hints.ai_next = NULL;
+
+		ret = getaddrinfo(address, NULL, &hints, &gai_result);
+		if (ret == 0 && gai_result)
+			memcpy(&port->raddr.addr, gai_result->ai_addr, gai_result->ai_addrlen);
+		else if (ret == EAI_NONAME)
+		{
+			struct addrinfo *gai_result2 = NULL;
+
+			port->remote_hostname = pstrdup(address);
+
+			ret = getaddrinfo(port->remote_hostname, NULL, NULL, &gai_result2);
+			if (ret == 0 && gai_result2)
+				memcpy(&port->raddr.addr, gai_result2->ai_addr, gai_result2->ai_addrlen);
+			else
+				ereport(ERROR,
+				(errmsg("getaddrinfo failed to look into hostname \"%s\": %s",
+						port->remote_hostname, gai_strerror(ret))));
+
+			if (gai_result2)
+				freeaddrinfo(gai_result2);
+		}
+		else
+			ereport(ERROR,
+					(errmsg("invalid IP address \"%s\": %s",
+							address, gai_strerror(ret))));
+		if (gai_result)
+			freeaddrinfo(gai_result);
+	}
+	else
+		port->raddr.addr.ss_family = AF_UNIX;
+
+	port->ssl_in_use = DatumGetBool(PG_GETARG_DATUM(3));
+
+	rsi->returnMode = SFRM_Materialize;
+
+	if (!load_hba())
+		ereport(ERROR,
+				(errmsg("failed to load pg_hba.conf file"),
+			   errhint("more details may be available in the server log.")));
+
+	/*
+	 * Create the tupledesc and tuplestore in the per_query context as
+	 * required for SFRM_Materialize.
+	 */
+	old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
+
+	tupdesc = CreateTemplateTupleDesc(NUM_PG_HBA_LOOKUP_ATTS, false);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "line_number",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "mode",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "type",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "keyword_database",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "database",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "keyword_user",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "user_name",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "keyword_address",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "address",
+					   INETOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "netmask",
+					   INETOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "hostname",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 12, "method",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 13, "options",
+					   JSONBOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 14, "reason",
+					   TEXTOID, -1, 0);
+	BlessTupleDesc(tupdesc);
+
+	tuple_store =
+		tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
+							  false, work_mem);
+
+	MemoryContextSwitchTo(old_cxt);
+
+	mycxt = (LookupHbaLineCxt *) palloc(sizeof(LookupHbaLineCxt));
+	mycxt->memcxt = AllocSetContextCreate(CurrentMemoryContext,
+										  "pg_hba_lookup tuple cxt",
+										  ALLOCSET_DEFAULT_MINSIZE,
+										  ALLOCSET_DEFAULT_INITSIZE,
+										  ALLOCSET_DEFAULT_MAXSIZE);
+	mycxt->tupdesc = tupdesc;
+	mycxt->tuple_store = tuple_store;
+
+	check_hba(port, lookup_hba_line_callback, mycxt);
+
+	MemoryContextDelete(mycxt->memcxt);
+	pfree(mycxt);
+
+	/* Clean up the memory allocated by load_hba() explicitly. */
+	discard_hba();
+
+	rsi->setDesc = tupdesc;
+	rsi->setResult = tuple_store;
+
+	PG_RETURN_NULL();
 }
diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c
index e1ceea6..171e532 100644
--- a/src/backend/utils/adt/jsonb_util.c
+++ b/src/backend/utils/adt/jsonb_util.c
@@ -1802,3 +1802,49 @@ uniqueifyJsonbObject(JsonbValue *object)
 		object->val.object.nPairs = res + 1 - object->val.object.pairs;
 	}
 }
+
+void
+push_jsonb_string_key(JsonbParseState **pstate, char *string_key)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvString;
+	jb.val.string.len = strlen(string_key);
+	jb.val.string.val = pstrdup(string_key);
+	pushJsonbValue(pstate, WJB_KEY, &jb);
+}
+
+void
+push_jsonb_bool_value(JsonbParseState **pstate, bool bool_val)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvBool;
+	jb.val.boolean = bool_val;
+
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
+
+void
+push_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val)
+{
+	JsonbValue	jb;
+	char		outputstr[64];
+
+	snprintf(outputstr, 64, "%d", int32_val);
+	jb.type = jbvNumeric;
+	jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1));
+
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
+
+void
+push_jsonb_string_value(JsonbParseState **pstate, char *string_value)
+{
+	JsonbValue	jb;
+
+	jb.type = jbvString;
+	jb.val.string.len = strlen(string_value);
+	jb.val.string.val = pstrdup(string_value);
+	pushJsonbValue(pstate, WJB_VALUE, &jb);
+}
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index b3f1bc4..a027fbe 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -192,18 +192,6 @@ PerformAuthentication(Port *port)
 	 * FIXME: [fork/exec] Ugh.  Is there a way around this overhead?
 	 */
 #ifdef EXEC_BACKEND
-	/*
-	 * load_hba() and load_ident() want to work within the PostmasterContext,
-	 * so create that if it doesn't exist (which it won't).  We'll delete it
-	 * again later, in PostgresMain.
-	 */
-	if (PostmasterContext == NULL)
-		PostmasterContext = AllocSetContextCreate(TopMemoryContext,
-												  "Postmaster",
-												  ALLOCSET_DEFAULT_MINSIZE,
-												  ALLOCSET_DEFAULT_INITSIZE,
-												  ALLOCSET_DEFAULT_MAXSIZE);
-
 	if (!load_hba())
 	{
 		/*
@@ -739,6 +727,14 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 	}
 
 	/*
+	 * We don't need the HBA and ident data going forward, but we can't rely
+	 * on release of PostmasterContext to clean that up, so discard them
+	 * explicitly here.
+	 */
+	discard_hba();
+	discard_ident();
+
+	/*
 	 * If we're trying to shut down, only superusers can connect, and new
 	 * replication connections are not allowed.
 	 */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index a595327..16f3f4a 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3014,6 +3014,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 = 3997 (  pg_hba_lookup PGNSP PGUID 12 1 1000 0 0 f f f f f t v u 4 0 2249 "25 25 25 16" "{25,25,25,16,23,25,25,1009,1009,1009,1009,25,869,869,25,25,3802,25}" "{i,i,i,i,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{database,user_name,address,ssl_inuse,line_number,mode,type,keyword_database,database,keyword_user,user_name,keyword_address,address,netmask,hostname,method,options,reason}" _null_ _null_ pg_hba_lookup _null_ _null_ _null_));
+DESCR("view client authentication settings");
 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 68a953a..291300b 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -98,6 +98,10 @@ typedef struct Port hbaPort;
 
 extern bool load_hba(void);
 extern bool load_ident(void);
+
+extern void discard_hba(void);
+extern void discard_ident(void);
+
 extern void hba_getauthmethod(hbaPort *port);
 extern int check_usermap(const char *usermap_name,
 			  const char *pg_role, const char *auth_user,
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 206288d..96e0564 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1159,6 +1159,9 @@ extern Datum pg_control_system(PG_FUNCTION_ARGS);
 extern Datum pg_control_init(PG_FUNCTION_ARGS);
 extern Datum pg_control_recovery(PG_FUNCTION_ARGS);
 
+/* hba.c */
+extern Datum pg_hba_lookup(PG_FUNCTION_ARGS);
+
 /* rls.c */
 extern Datum row_security_active(PG_FUNCTION_ARGS);
 extern Datum row_security_active_name(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 5f49d8d..33e00d2 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -427,6 +427,12 @@ extern bool JsonbDeepContains(JsonbIterator **val,
 				  JsonbIterator **mContained);
 extern void JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash);
 
+/* jsonb_util.c support functions */
+void push_jsonb_string_key(JsonbParseState **pstate, char *string_key);
+void push_jsonb_bool_value(JsonbParseState **pstate, bool bool_val);
+void push_jsonb_int32_value(JsonbParseState **pstate, int32 int32_val);
+void push_jsonb_string_value(JsonbParseState **pstate, char *string_value);
+
 /* jsonb.c support functions */
 extern char *JsonbToCString(StringInfo out, JsonbContainer *in,
 			   int estimated_len);
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index b850db0..fc32551 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1073,6 +1073,7 @@ LogicalOutputPluginWriterWrite
 LogicalRewriteMappingData
 LogicalTape
 LogicalTapeSet
+LookupHbaLineCxt
 MAGIC
 MBuf
 MEMORY_BASIC_INFORMATION
#63Robert Haas
robertmhaas@gmail.com
In reply to: Haribabu Kommi (#62)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Mon, Mar 21, 2016 at 3:31 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

On Mon, Mar 21, 2016 at 2:00 PM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

Haribabu Kommi wrote:

Check.

+} lookup_hba_line_context;
^^^^^ but why TAB here?

corrected. I am not sure why pg_indent is adding a tab here.

It's because lookup_hba_line_context is not listed in typedefs.list.
I suggest adding it and all other new typedefs you add, and rerunning
pgindent, as the lack of those may affect other places where those names
appear.

Thanks for the details. I added the new typedef into typedefs.list file.
Updated patch is attached.

This patch is still marked "needs review". If it's ready to go, one
of the reviewers should mark it "ready for committer".

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#64David Steele
david@pgmasters.net
In reply to: Robert Haas (#63)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On 4/5/16 9:52 PM, Robert Haas wrote:

On Mon, Mar 21, 2016 at 3:31 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

On Mon, Mar 21, 2016 at 2:00 PM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

Haribabu Kommi wrote:

Check.

+} lookup_hba_line_context;
^^^^^ but why TAB here?

corrected. I am not sure why pg_indent is adding a tab here.

It's because lookup_hba_line_context is not listed in typedefs.list.
I suggest adding it and all other new typedefs you add, and rerunning
pgindent, as the lack of those may affect other places where those names
appear.

Thanks for the details. I added the new typedef into typedefs.list file.
Updated patch is attached.

This patch is still marked "needs review". If it's ready to go, one
of the reviewers should mark it "ready for committer".

Can one of the reviewers decide if this is ready to commit? I fear it
will be pushed to the next CF otherwise. I don't think the committers
have time to make that determination today...

--
-David
david@pgmasters.net

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#65Robert Haas
robertmhaas@gmail.com
In reply to: David Steele (#64)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Fri, Apr 8, 2016 at 3:24 PM, David Steele <david@pgmasters.net> wrote:

On 4/5/16 9:52 PM, Robert Haas wrote:

On Mon, Mar 21, 2016 at 3:31 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

On Mon, Mar 21, 2016 at 2:00 PM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

Haribabu Kommi wrote:

Check.

+} lookup_hba_line_context;
^^^^^ but why TAB here?

corrected. I am not sure why pg_indent is adding a tab here.

It's because lookup_hba_line_context is not listed in typedefs.list.
I suggest adding it and all other new typedefs you add, and rerunning
pgindent, as the lack of those may affect other places where those names
appear.

Thanks for the details. I added the new typedef into typedefs.list file.
Updated patch is attached.

This patch is still marked "needs review". If it's ready to go, one
of the reviewers should mark it "ready for committer".

Can one of the reviewers decide if this is ready to commit? I fear it
will be pushed to the next CF otherwise. I don't think the committers
have time to make that determination today...

Well, it's not getting committed unless some committer determines that
it is ready to commit. As far as 9.6 goes, that committer will not be
me; my commit bit is starting to smoke, and anything I try to do in
the next few hours is likely to have an unacceptable error rate. I am
beat. But I think we have a good release to look forward to.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#66Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#65)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

Robert Haas <robertmhaas@gmail.com> writes:

On Fri, Apr 8, 2016 at 3:24 PM, David Steele <david@pgmasters.net> wrote:

Can one of the reviewers decide if this is ready to commit? I fear it
will be pushed to the next CF otherwise. I don't think the committers
have time to make that determination today...

Well, it's not getting committed unless some committer determines that
it is ready to commit.

I took a quick look; IMO it is certainly not ready to commit.

* Why is the IP address parameter declared as "text" and not "inet"?
Why is it optional --- it doesn't seem to me that it can have a useful
default value?

* Docs claim that ssl_inuse is of type text, when it's really bool,
and that the result is record when it's really setof record.

* While I agree that allowing ssl_inuse to default to false is probably
okay, I wonder how well this function signature will cope if we ever add
more things that pg_hba matching depends on.

* The patch seems mighty invasive to the auth code, which is not really
a place where we want a lot of churn and opportunity for mistakes. Is
there another way to do this? Do we really *need* a line-by-line report
of why specific lines didn't match?

* I'm a tad suspicious of the memory management, in particular the
random-looking rearrangement of where PostmasterContext gets created
and deleted. It'd likely be better to fix things so that load_hba
doesn't have a hard-wired reference to PostmasterContext in the first
place, but is told which context to allocate under.

More generally, I'm not convinced about the use-case for this patch.
What problem is it supposed to help in dealing with, exactly? Not syntax
errors in the hba file, evidently, since it doesn't make any attempt to
instrument the file parser. And it's not very clear that it'll help
with "I can't connect", either, because if you can't connect you're
not going to be running this function.

If people actually need more help in figuring out why the hba line matcher
selected the line it did, I'm inclined to think maybe what's needed is a
logging option that would print a verbose trace of match/no-match
decisions. More or less the same data this returns, really, but directed
to the postmaster log. That way you'd be able to see it even when you're
not getting let into the database.

On the whole, though, I wonder if we shouldn't just tweak log_connections
so that it records which pg_hba line was matched. (We already have
logging about which line was matched on an auth failure.) I'm not really
convinced that a SQL function like this is going to be very helpful.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#67Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#66)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Fri, Apr 8, 2016 at 4:36 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

On Fri, Apr 8, 2016 at 3:24 PM, David Steele <david@pgmasters.net> wrote:

Can one of the reviewers decide if this is ready to commit? I fear it
will be pushed to the next CF otherwise. I don't think the committers
have time to make that determination today...

Well, it's not getting committed unless some committer determines that
it is ready to commit.

I took a quick look; IMO it is certainly not ready to commit.

OK, marked Returned with Feedback. Hopefully the patch authors will
find your feedback useful.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#68Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Tom Lane (#66)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

On Sat, Apr 9, 2016 at 6:36 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

More generally, I'm not convinced about the use-case for this patch.
What problem is it supposed to help in dealing with, exactly? Not syntax
errors in the hba file, evidently, since it doesn't make any attempt to
instrument the file parser. And it's not very clear that it'll help
with "I can't connect", either, because if you can't connect you're
not going to be running this function.

Apologies for replying an old thread.

The main reason behind of this patch is for the administrators to control
and verify the authentication mechanism that was deployed for several
users in the database is correctly picking the assigned hba config or not?

I feel this SQL function is useful for administrators and not for normal
users.

If anyone is not against to the above use case, i will update the patch based
on the review comments and post it later.

Regards,
Hari Babu
Fujitsu Australia

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#69Tom Lane
tgl@sss.pgh.pa.us
In reply to: Haribabu Kommi (#68)
Re: pg_hba_lookup function to get all matching pg_hba.conf entries

Haribabu Kommi <kommi.haribabu@gmail.com> writes:

On Sat, Apr 9, 2016 at 6:36 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

More generally, I'm not convinced about the use-case for this patch.
What problem is it supposed to help in dealing with, exactly? Not syntax
errors in the hba file, evidently, since it doesn't make any attempt to
instrument the file parser. And it's not very clear that it'll help
with "I can't connect", either, because if you can't connect you're
not going to be running this function.

Apologies for replying an old thread.

The main reason behind of this patch is for the administrators to control
and verify the authentication mechanism that was deployed for several
users in the database is correctly picking the assigned hba config or not?

That doesn't really answer my question: what is a concrete use-case for
this function? Reproducing the same behavior that would happen during
a login attempt does not seem terribly useful from here, because you
could simply attempt to log in, instead. As I said upthread, maybe we
need a bit more logging in the authentication logic, but that doesn't
seem to lead to wanting a SQL function.

What I actually think we could use is something like the pg_file_settings
view, but for pg_hba.conf. In particular, pg_file_settings has a specific
charter of being able to detect syntax errors in not-yet-loaded config
files. That seems like clearly useful functionality to me, but we don't
have it for pg_hba.conf (and this patch didn't add it).

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers