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

> On Mon, Dec 28, 2015 at 9:09 PM, Shulgin, Oleksandr
> <oleksandr.shul...@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
From 56b584c25b952d6855d2a3d77eb40755d982efb9 Mon Sep 17 00:00:00 2001
From: Oleksandr Shulgin <oleksandr.shul...@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

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

Reply via email to