public inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH v7 2/2] POC: Add a pg_hba_matches() function.
18+ messages / 4 participants
[nested] [flat]

* [PATCH v8 6/6] POC: Add a pg_hba_matches() function.
@ 2022-02-22 13:34 Julien Rouhaud <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Julien Rouhaud @ 2022-02-22 13:34 UTC (permalink / raw)

Catversion is bumped.

Author: Julien Rouhaud
Reviewed-by: FIXME
Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya%40jrouhaud
---
 src/backend/catalog/system_functions.sql |   9 ++
 src/backend/libpq/hba.c                  | 138 +++++++++++++++++++++++
 src/include/catalog/pg_proc.dat          |   7 ++
 3 files changed, 154 insertions(+)

diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 73da687d5d..bfee72f705 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -594,6 +594,15 @@ LANGUAGE internal
 STRICT IMMUTABLE PARALLEL SAFE
 AS 'unicode_is_normalized';
 
+CREATE OR REPLACE FUNCTION
+  pg_hba_matches(
+    IN address inet, IN role text, IN ssl bool DEFAULT false,
+    OUT file_name text, OUT line_num int4, OUT raw_line text)
+RETURNS RECORD
+LANGUAGE INTERNAL
+VOLATILE
+AS 'pg_hba_matches';
+
 --
 -- The default permissions for functions mean that anyone can execute them.
 -- A number of functions shouldn't be executable by just anyone, but rather
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 814bfcf30d..70e7c871a8 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -27,6 +27,7 @@
 #include <unistd.h>
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "common/ip.h"
@@ -42,6 +43,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/inet.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/varlena.h"
@@ -2983,3 +2985,139 @@ hba_authname(UserAuth auth_method)
 
 	return UserAuthName[auth_method];
 }
+
+#define PG_HBA_MATCHES_ATTS	3
+
+/*
+ * SQL-accessible SRF to return the entries that match the given connection
+ * info, if any.
+ */
+Datum pg_hba_matches(PG_FUNCTION_ARGS)
+{
+	MemoryContext ctxt;
+	inet	   *address = NULL;
+	bool		ssl_in_use = false;
+	hbaPort	   *port = palloc0(sizeof(hbaPort));
+	TupleDesc	tupdesc;
+	Datum		values[PG_HBA_MATCHES_ATTS];
+	bool		isnull[PG_HBA_MATCHES_ATTS];
+
+	if (!is_member_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("only superuser or a member of the pg_read_server_files role may call this function")));
+
+	if (PG_ARGISNULL(0))
+		port->raddr.addr.ss_family = AF_UNIX;
+	else
+	{
+		int			bits;
+		char	   *ptr;
+		char		tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+		address = PG_GETARG_INET_PP(0);
+
+		bits = ip_maxbits(address) - ip_bits(address);
+		if (bits != 0)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("Invalid address")));
+		}
+
+		/* force display of max bits, regardless of masklen... */
+		if (pg_inet_net_ntop(ip_family(address), ip_addr(address),
+							 ip_maxbits(address), tmp, sizeof(tmp)) == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+					 errmsg("could not format inet value: %m")));
+
+		/* Suppress /n if present (shouldn't happen now) */
+		if ((ptr = strchr(tmp, '/')) != NULL)
+			*ptr = '\0';
+
+		switch (ip_family(address))
+		{
+			case PGSQL_AF_INET:
+			{
+				struct sockaddr_in *dst;
+
+				dst = (struct sockaddr_in *) &port->raddr.addr;
+				dst->sin_family = AF_INET;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin_addr, &ip_addr(address), sizeof(dst->sin_addr));
+
+				break;
+			}
+			/* See pg_inet_net_ntop() for details about those constants */
+			case PGSQL_AF_INET6:
+#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6
+			case AF_INET6:
+#endif
+			{
+				struct sockaddr_in6 *dst;
+
+				dst = (struct sockaddr_in6 *) &port->raddr.addr;
+				dst->sin6_family = AF_INET6;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin6_addr, &ip_addr(address), sizeof(dst->sin6_addr));
+
+				break;
+			}
+			default:
+				elog(ERROR, "unexpected ip_family: %d", ip_family(address));
+				break;
+		}
+	}
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("parameter role is mandatory")));
+	port->user_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
+
+	if (!PG_ARGISNULL(2))
+		ssl_in_use = PG_GETARG_BOOL(2);
+
+	port->ssl_in_use = ssl_in_use;
+
+	tupdesc = CreateTemplateTupleDesc(PG_HBA_MATCHES_ATTS);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "file_name",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "line_num",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "raw_line",
+					   TEXTOID, -1, 0);
+
+	BlessTupleDesc(tupdesc);
+
+	memset(isnull, 0, sizeof(isnull));
+
+	/* FIXME rework API to not rely on PostmasterContext */
+	ctxt = AllocSetContextCreate(CurrentMemoryContext, "load_hba",
+								 ALLOCSET_DEFAULT_SIZES);
+	PostmasterContext = AllocSetContextCreate(ctxt,
+											  "Postmaster",
+											  ALLOCSET_DEFAULT_SIZES);
+	parsed_hba_context = NULL;
+	if (!load_hba())
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("Invalidation auth configuration file")));
+
+	check_hba(port);
+
+	if (port->hba->auth_method == uaImplicitReject)
+		PG_RETURN_NULL();
+
+	values[0] = CStringGetTextDatum(port->hba->sourcefile);
+	values[1] = Int32GetDatum(port->hba->linenumber);
+	values[2] = CStringGetTextDatum(port->hba->rawline);
+
+	MemoryContextDelete(PostmasterContext);
+	PostmasterContext = NULL;
+
+	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d66b2443a4..f6609a4a5f 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6139,6 +6139,13 @@
   proargmodes => '{o,o,o,o,o,o,o}',
   proargnames => '{mapping_number,file_name,line_number,map_name,sys_name,pg_username,error}',
   prosrc => 'pg_ident_file_mappings' },
+{ oid => '9557', descr => 'show wether the given connection would match an hba line',
+  proname => 'pg_hba_matches', provolatile => 'v', prorettype => 'record',
+  proargtypes => 'inet text bool', proisstrict => 'f',
+  proallargtypes => '{inet,text,bool,text,int4,text}',
+  proargmodes => '{i,i,i,o,o,o}',
+  proargnames => '{address,role,ssl,file_name,line_num,raw_line}',
+  prosrc => 'pg_hba_matches' },
 { oid => '1371', descr => 'view system lock information',
   proname => 'pg_lock_status', prorows => '1000', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
-- 
2.37.0


--lqayot32rzm7n4sf--





^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* [PATCH v7 2/2] POC: Add a pg_hba_matches() function.
@ 2022-02-22 13:34 Julien Rouhaud <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Julien Rouhaud @ 2022-02-22 13:34 UTC (permalink / raw)

Catversion is bumped.

Author: Julien Rouhaud
Reviewed-by: FIXME
Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya%40jrouhaud
---
 src/backend/catalog/system_functions.sql |   9 ++
 src/backend/libpq/hba.c                  | 138 +++++++++++++++++++++++
 src/include/catalog/pg_proc.dat          |   7 ++
 3 files changed, 154 insertions(+)

diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 73da687d5d..bfee72f705 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -594,6 +594,15 @@ LANGUAGE internal
 STRICT IMMUTABLE PARALLEL SAFE
 AS 'unicode_is_normalized';
 
+CREATE OR REPLACE FUNCTION
+  pg_hba_matches(
+    IN address inet, IN role text, IN ssl bool DEFAULT false,
+    OUT file_name text, OUT line_num int4, OUT raw_line text)
+RETURNS RECORD
+LANGUAGE INTERNAL
+VOLATILE
+AS 'pg_hba_matches';
+
 --
 -- The default permissions for functions mean that anyone can execute them.
 -- A number of functions shouldn't be executable by just anyone, but rather
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 56b6cec9d5..09554a9bc6 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -27,6 +27,7 @@
 #include <unistd.h>
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "common/ip.h"
@@ -42,6 +43,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/inet.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/varlena.h"
@@ -2946,3 +2948,139 @@ hba_authname(UserAuth auth_method)
 
 	return UserAuthName[auth_method];
 }
+
+#define PG_HBA_MATCHES_ATTS	3
+
+/*
+ * SQL-accessible SRF to return the entries that match the given connection
+ * info, if any.
+ */
+Datum pg_hba_matches(PG_FUNCTION_ARGS)
+{
+	MemoryContext ctxt;
+	inet	   *address = NULL;
+	bool		ssl_in_use = false;
+	hbaPort	   *port = palloc0(sizeof(hbaPort));
+	TupleDesc	tupdesc;
+	Datum		values[PG_HBA_MATCHES_ATTS];
+	bool		isnull[PG_HBA_MATCHES_ATTS];
+
+	if (!is_member_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("only superuser or a member of the pg_read_server_files role may call this function")));
+
+	if (PG_ARGISNULL(0))
+		port->raddr.addr.ss_family = AF_UNIX;
+	else
+	{
+		int			bits;
+		char	   *ptr;
+		char		tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+		address = PG_GETARG_INET_PP(0);
+
+		bits = ip_maxbits(address) - ip_bits(address);
+		if (bits != 0)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("Invalid address")));
+		}
+
+		/* force display of max bits, regardless of masklen... */
+		if (pg_inet_net_ntop(ip_family(address), ip_addr(address),
+							 ip_maxbits(address), tmp, sizeof(tmp)) == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+					 errmsg("could not format inet value: %m")));
+
+		/* Suppress /n if present (shouldn't happen now) */
+		if ((ptr = strchr(tmp, '/')) != NULL)
+			*ptr = '\0';
+
+		switch (ip_family(address))
+		{
+			case PGSQL_AF_INET:
+			{
+				struct sockaddr_in *dst;
+
+				dst = (struct sockaddr_in *) &port->raddr.addr;
+				dst->sin_family = AF_INET;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin_addr, &ip_addr(address), sizeof(dst->sin_addr));
+
+				break;
+			}
+			/* See pg_inet_net_ntop() for details about those constants */
+			case PGSQL_AF_INET6:
+#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6
+			case AF_INET6:
+#endif
+			{
+				struct sockaddr_in6 *dst;
+
+				dst = (struct sockaddr_in6 *) &port->raddr.addr;
+				dst->sin6_family = AF_INET6;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin6_addr, &ip_addr(address), sizeof(dst->sin6_addr));
+
+				break;
+			}
+			default:
+				elog(ERROR, "unexpected ip_family: %d", ip_family(address));
+				break;
+		}
+	}
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("parameter role is mandatory")));
+	port->user_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
+
+	if (!PG_ARGISNULL(2))
+		ssl_in_use = PG_GETARG_BOOL(2);
+
+	port->ssl_in_use = ssl_in_use;
+
+	tupdesc = CreateTemplateTupleDesc(PG_HBA_MATCHES_ATTS);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "file_name",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "line_num",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "raw_line",
+					   TEXTOID, -1, 0);
+
+	BlessTupleDesc(tupdesc);
+
+	memset(isnull, 0, sizeof(isnull));
+
+	/* FIXME rework API to not rely on PostmasterContext */
+	ctxt = AllocSetContextCreate(CurrentMemoryContext, "load_hba",
+								 ALLOCSET_DEFAULT_SIZES);
+	PostmasterContext = AllocSetContextCreate(ctxt,
+											  "Postmaster",
+											  ALLOCSET_DEFAULT_SIZES);
+	parsed_hba_context = NULL;
+	if (!load_hba())
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("Invalidation auth configuration file")));
+
+	check_hba(port);
+
+	if (port->hba->auth_method == uaImplicitReject)
+		PG_RETURN_NULL();
+
+	values[0] = CStringGetTextDatum(port->hba->sourcefile);
+	values[1] = Int32GetDatum(port->hba->linenumber);
+	values[2] = CStringGetTextDatum(port->hba->rawline);
+
+	MemoryContextDelete(PostmasterContext);
+	PostmasterContext = NULL;
+
+	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 65bfd32753..2c65ee2019 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6139,6 +6139,13 @@
   proargmodes => '{o,o,o,o,o,o,o}',
   proargnames => '{mapping_number,file_name,line_number,map_name,sys_name,pg_username,error}',
   prosrc => 'pg_ident_file_mappings' },
+{ oid => '9557', descr => 'show wether the given connection would match an hba line',
+  proname => 'pg_hba_matches', provolatile => 'v', prorettype => 'record',
+  proargtypes => 'inet text bool', proisstrict => 'f',
+  proallargtypes => '{inet,text,bool,text,int4,text}',
+  proargmodes => '{i,i,i,o,o,o}',
+  proargnames => '{address,role,ssl,file_name,line_num,raw_line}',
+  prosrc => 'pg_hba_matches' },
 { oid => '1371', descr => 'view system lock information',
   proname => 'pg_lock_status', prorows => '1000', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
-- 
2.33.1


--o45jk7yo2ulu7ifa--





^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* [PATCH v10 3/3] POC: Add a pg_hba_matches() function.
@ 2022-02-22 13:34 Julien Rouhaud <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Julien Rouhaud @ 2022-02-22 13:34 UTC (permalink / raw)

Catversion is bumped.

Author: Julien Rouhaud
Reviewed-by: FIXME
Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya%40jrouhaud
---
 src/backend/catalog/system_functions.sql |   9 ++
 src/backend/libpq/hba.c                  | 138 +++++++++++++++++++++++
 src/include/catalog/pg_proc.dat          |   7 ++
 3 files changed, 154 insertions(+)

diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 30a048f6b0..224b7a483a 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -594,6 +594,15 @@ LANGUAGE internal
 STRICT IMMUTABLE PARALLEL SAFE
 AS 'unicode_is_normalized';
 
+CREATE OR REPLACE FUNCTION
+  pg_hba_matches(
+    IN address inet, IN role text, IN ssl bool DEFAULT false,
+    OUT file_name text, OUT line_num int4, OUT raw_line text)
+RETURNS RECORD
+LANGUAGE INTERNAL
+VOLATILE
+AS 'pg_hba_matches';
+
 --
 -- The default permissions for functions mean that anyone can execute them.
 -- A number of functions shouldn't be executable by just anyone, but rather
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 071bf1ff95..02a7164225 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -28,6 +28,7 @@
 #include <unistd.h>
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "common/ip.h"
@@ -43,6 +44,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/inet.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/varlena.h"
@@ -2969,3 +2971,139 @@ hba_authname(UserAuth auth_method)
 
 	return UserAuthName[auth_method];
 }
+
+#define PG_HBA_MATCHES_ATTS	3
+
+/*
+ * SQL-accessible SRF to return the entries that match the given connection
+ * info, if any.
+ */
+Datum pg_hba_matches(PG_FUNCTION_ARGS)
+{
+	MemoryContext ctxt;
+	inet	   *address = NULL;
+	bool		ssl_in_use = false;
+	hbaPort	   *port = palloc0(sizeof(hbaPort));
+	TupleDesc	tupdesc;
+	Datum		values[PG_HBA_MATCHES_ATTS];
+	bool		isnull[PG_HBA_MATCHES_ATTS];
+
+	if (!is_member_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("only superuser or a member of the pg_read_server_files role may call this function")));
+
+	if (PG_ARGISNULL(0))
+		port->raddr.addr.ss_family = AF_UNIX;
+	else
+	{
+		int			bits;
+		char	   *ptr;
+		char		tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+		address = PG_GETARG_INET_PP(0);
+
+		bits = ip_maxbits(address) - ip_bits(address);
+		if (bits != 0)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("Invalid address")));
+		}
+
+		/* force display of max bits, regardless of masklen... */
+		if (pg_inet_net_ntop(ip_family(address), ip_addr(address),
+							 ip_maxbits(address), tmp, sizeof(tmp)) == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+					 errmsg("could not format inet value: %m")));
+
+		/* Suppress /n if present (shouldn't happen now) */
+		if ((ptr = strchr(tmp, '/')) != NULL)
+			*ptr = '\0';
+
+		switch (ip_family(address))
+		{
+			case PGSQL_AF_INET:
+			{
+				struct sockaddr_in *dst;
+
+				dst = (struct sockaddr_in *) &port->raddr.addr;
+				dst->sin_family = AF_INET;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin_addr, &ip_addr(address), sizeof(dst->sin_addr));
+
+				break;
+			}
+			/* See pg_inet_net_ntop() for details about those constants */
+			case PGSQL_AF_INET6:
+#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6
+			case AF_INET6:
+#endif
+			{
+				struct sockaddr_in6 *dst;
+
+				dst = (struct sockaddr_in6 *) &port->raddr.addr;
+				dst->sin6_family = AF_INET6;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin6_addr, &ip_addr(address), sizeof(dst->sin6_addr));
+
+				break;
+			}
+			default:
+				elog(ERROR, "unexpected ip_family: %d", ip_family(address));
+				break;
+		}
+	}
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("parameter role is mandatory")));
+	port->user_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
+
+	if (!PG_ARGISNULL(2))
+		ssl_in_use = PG_GETARG_BOOL(2);
+
+	port->ssl_in_use = ssl_in_use;
+
+	tupdesc = CreateTemplateTupleDesc(PG_HBA_MATCHES_ATTS);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "file_name",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "line_num",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "raw_line",
+					   TEXTOID, -1, 0);
+
+	BlessTupleDesc(tupdesc);
+
+	memset(isnull, 0, sizeof(isnull));
+
+	/* FIXME rework API to not rely on PostmasterContext */
+	ctxt = AllocSetContextCreate(CurrentMemoryContext, "load_hba",
+								 ALLOCSET_DEFAULT_SIZES);
+	PostmasterContext = AllocSetContextCreate(ctxt,
+											  "Postmaster",
+											  ALLOCSET_DEFAULT_SIZES);
+	parsed_hba_context = NULL;
+	if (!load_hba())
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("Invalidation auth configuration file")));
+
+	check_hba(port);
+
+	if (port->hba->auth_method == uaImplicitReject)
+		PG_RETURN_NULL();
+
+	values[0] = CStringGetTextDatum(port->hba->sourcefile);
+	values[1] = Int32GetDatum(port->hba->linenumber);
+	values[2] = CStringGetTextDatum(port->hba->rawline);
+
+	MemoryContextDelete(PostmasterContext);
+	PostmasterContext = NULL;
+
+	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index e6a54cc3d6..d78c5a7556 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6139,6 +6139,13 @@
   proargmodes => '{o,o,o,o,o,o,o}',
   proargnames => '{mapping_number,file_name,line_number,map_name,sys_name,pg_username,error}',
   prosrc => 'pg_ident_file_mappings' },
+{ oid => '9557', descr => 'show wether the given connection would match an hba line',
+  proname => 'pg_hba_matches', provolatile => 'v', prorettype => 'record',
+  proargtypes => 'inet text bool', proisstrict => 'f',
+  proallargtypes => '{inet,text,bool,text,int4,text}',
+  proargmodes => '{i,i,i,o,o,o}',
+  proargnames => '{address,role,ssl,file_name,line_num,raw_line}',
+  prosrc => 'pg_hba_matches' },
 { oid => '1371', descr => 'view system lock information',
   proname => 'pg_lock_status', prorows => '1000', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
-- 
2.37.0


--fy3hxyvyv2spu3wq--





^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* [PATCH v1 3/3] POC: Add a pg_hba_matches() function.
@ 2022-02-22 13:34 Julien Rouhaud <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Julien Rouhaud @ 2022-02-22 13:34 UTC (permalink / raw)

Author: Julien Rouhaud
Reviewed-by: FIXME
Discussion: FIXME
---
 src/backend/catalog/system_functions.sql |   9 ++
 src/backend/libpq/hba.c                  | 127 +++++++++++++++++++++++
 src/include/catalog/pg_proc.dat          |   7 ++
 3 files changed, 143 insertions(+)

diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index fd1421788e..ae839a4b76 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -594,6 +594,15 @@ LANGUAGE internal
 STRICT IMMUTABLE PARALLEL SAFE
 AS 'unicode_is_normalized';
 
+CREATE OR REPLACE FUNCTION
+  pg_hba_matches(
+    IN address inet, IN role text, IN ssl bool DEFAULT false,
+    OUT file_name text, OUT line_num int4, OUT raw_line text)
+RETURNS RECORD
+LANGUAGE INTERNAL
+VOLATILE
+AS 'pg_hba_matches';
+
 --
 -- The default permissions for functions mean that anyone can execute them.
 -- A number of functions shouldn't be executable by just anyone, but rather
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 8b72141342..42fcf9edca 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -41,6 +41,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/inet.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/varlena.h"
@@ -3471,3 +3472,129 @@ pg_ident_file_mappings(PG_FUNCTION_ARGS)
 
 	PG_RETURN_NULL();
 }
+
+#define PG_HBA_MATCHES_ATTS	3
+
+/*
+ * SQL-accessible SRF to return the entries that match the given connection
+ * info, if any.
+ */
+Datum pg_hba_matches(PG_FUNCTION_ARGS)
+{
+	MemoryContext ctxt;
+	inet	   *address = NULL;
+	bool		ssl_in_use = false;
+	hbaPort	   *port = palloc0(sizeof(hbaPort));
+	TupleDesc	tupdesc;
+	Datum		values[PG_HBA_MATCHES_ATTS];
+	bool		isnull[PG_HBA_MATCHES_ATTS];
+
+	if (PG_ARGISNULL(0))
+		port->raddr.addr.ss_family = AF_UNIX;
+	else
+	{
+		int			bits;
+		char	   *ptr;
+		char		tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+		address = PG_GETARG_INET_PP(0);
+
+		bits = ip_maxbits(address) - ip_bits(address);
+		if (bits != 0)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("Invalid address")));
+		}
+
+		/* force display of max bits, regardless of masklen... */
+		if (pg_inet_net_ntop(ip_family(address), ip_addr(address),
+							 ip_maxbits(address), tmp, sizeof(tmp)) == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+					 errmsg("could not format inet value: %m")));
+
+		/* Suppress /n if present (shouldn't happen now) */
+		if ((ptr = strchr(tmp, '/')) != NULL)
+			*ptr = '\0';
+
+
+		/* See pg_inet_net_ntop() for details about those constants */
+		switch (ip_family(address))
+		{
+			case PGSQL_AF_INET:
+			{
+				struct sockaddr_in *dst;
+
+				dst = (struct sockaddr_in *) &port->raddr.addr;
+				dst->sin_family = AF_INET;
+				inet_pton(AF_INET, tmp, &dst->sin_addr);
+				break;
+			}
+			case PGSQL_AF_INET6:
+#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6
+			case AF_INET6:
+			{
+				struct sockaddr_in6 *dst;
+
+				dst = (struct sockaddr_in6 *) &port->raddr.addr;
+				dst->sin6_family = AF_INET6;
+				inet_pton(AF_INET6, tmp, &dst->sin6_addr);
+				break;
+			}
+#endif
+			default:
+				elog(ERROR, "unexpected ip_family: %d", ip_family(address));
+				break;
+		}
+	}
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("parameter role is mandatory")));
+	port->user_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
+
+	if (!PG_ARGISNULL(2))
+		ssl_in_use = PG_GETARG_BOOL(2);
+
+	port->ssl_in_use = ssl_in_use;
+
+	tupdesc = CreateTemplateTupleDesc(PG_HBA_MATCHES_ATTS);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "file_name",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "line_num",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "raw_line",
+					   TEXTOID, -1, 0);
+
+	BlessTupleDesc(tupdesc);
+
+	memset(isnull, 0, sizeof(isnull));
+
+	/* FIXME rework API to not rely on PostmasterContext */
+	ctxt = AllocSetContextCreate(CurrentMemoryContext, "load_hba",
+								 ALLOCSET_DEFAULT_SIZES);
+	PostmasterContext = AllocSetContextCreate(ctxt,
+											  "Postmaster",
+											  ALLOCSET_DEFAULT_SIZES);
+	parsed_hba_context = NULL;
+	if (!load_hba())
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("Invalidation auth configuration file")));
+
+	check_hba(port);
+
+	if (port->hba->auth_method == uaImplicitReject)
+		PG_RETURN_NULL();
+
+	values[0] = CStringGetTextDatum(port->hba->sourcefile);
+	values[1] = Int32GetDatum(port->hba->linenumber);
+	values[2] = CStringGetTextDatum(port->hba->rawline);
+
+	MemoryContextDelete(PostmasterContext);
+	PostmasterContext = NULL;
+
+	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 2292115c85..b33cad2848 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6128,6 +6128,13 @@
   proargmodes => '{o,o,o,o,o,o,o}',
   proargnames => '{mapping_number,file_name,line_number,map_name,sys_name,pg_usernamee,error}',
   prosrc => 'pg_ident_file_mappings' },
+{ oid => '9557', descr => 'show wether the given connection would match an hba line',
+  proname => 'pg_hba_matches', provolatile => 'v', prorettype => 'record',
+  proargtypes => 'inet text bool', proisstrict => 'f',
+  proallargtypes => '{inet,text,bool,text,int4,text}',
+  proargmodes => '{i,i,i,o,o,o}',
+  proargnames => '{address,role,ssl,file_name,line_num,raw_line}',
+  prosrc => 'pg_hba_matches' },
 { oid => '1371', descr => 'view system lock information',
   proname => 'pg_lock_status', prorows => '1000', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
-- 
2.33.1


--j3tmyqztg67irqsk--





^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* [PATCH v10 3/3] POC: Add a pg_hba_matches() function.
@ 2022-02-22 13:34 Julien Rouhaud <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Julien Rouhaud @ 2022-02-22 13:34 UTC (permalink / raw)

Catversion is bumped.

Author: Julien Rouhaud
Reviewed-by: FIXME
Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya%40jrouhaud
---
 src/backend/catalog/system_functions.sql |   9 ++
 src/backend/libpq/hba.c                  | 138 +++++++++++++++++++++++
 src/include/catalog/pg_proc.dat          |   7 ++
 3 files changed, 154 insertions(+)

diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 30a048f6b0..224b7a483a 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -594,6 +594,15 @@ LANGUAGE internal
 STRICT IMMUTABLE PARALLEL SAFE
 AS 'unicode_is_normalized';
 
+CREATE OR REPLACE FUNCTION
+  pg_hba_matches(
+    IN address inet, IN role text, IN ssl bool DEFAULT false,
+    OUT file_name text, OUT line_num int4, OUT raw_line text)
+RETURNS RECORD
+LANGUAGE INTERNAL
+VOLATILE
+AS 'pg_hba_matches';
+
 --
 -- The default permissions for functions mean that anyone can execute them.
 -- A number of functions shouldn't be executable by just anyone, but rather
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 071bf1ff95..02a7164225 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -28,6 +28,7 @@
 #include <unistd.h>
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "common/ip.h"
@@ -43,6 +44,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/inet.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/varlena.h"
@@ -2969,3 +2971,139 @@ hba_authname(UserAuth auth_method)
 
 	return UserAuthName[auth_method];
 }
+
+#define PG_HBA_MATCHES_ATTS	3
+
+/*
+ * SQL-accessible SRF to return the entries that match the given connection
+ * info, if any.
+ */
+Datum pg_hba_matches(PG_FUNCTION_ARGS)
+{
+	MemoryContext ctxt;
+	inet	   *address = NULL;
+	bool		ssl_in_use = false;
+	hbaPort	   *port = palloc0(sizeof(hbaPort));
+	TupleDesc	tupdesc;
+	Datum		values[PG_HBA_MATCHES_ATTS];
+	bool		isnull[PG_HBA_MATCHES_ATTS];
+
+	if (!is_member_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("only superuser or a member of the pg_read_server_files role may call this function")));
+
+	if (PG_ARGISNULL(0))
+		port->raddr.addr.ss_family = AF_UNIX;
+	else
+	{
+		int			bits;
+		char	   *ptr;
+		char		tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+		address = PG_GETARG_INET_PP(0);
+
+		bits = ip_maxbits(address) - ip_bits(address);
+		if (bits != 0)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("Invalid address")));
+		}
+
+		/* force display of max bits, regardless of masklen... */
+		if (pg_inet_net_ntop(ip_family(address), ip_addr(address),
+							 ip_maxbits(address), tmp, sizeof(tmp)) == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+					 errmsg("could not format inet value: %m")));
+
+		/* Suppress /n if present (shouldn't happen now) */
+		if ((ptr = strchr(tmp, '/')) != NULL)
+			*ptr = '\0';
+
+		switch (ip_family(address))
+		{
+			case PGSQL_AF_INET:
+			{
+				struct sockaddr_in *dst;
+
+				dst = (struct sockaddr_in *) &port->raddr.addr;
+				dst->sin_family = AF_INET;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin_addr, &ip_addr(address), sizeof(dst->sin_addr));
+
+				break;
+			}
+			/* See pg_inet_net_ntop() for details about those constants */
+			case PGSQL_AF_INET6:
+#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6
+			case AF_INET6:
+#endif
+			{
+				struct sockaddr_in6 *dst;
+
+				dst = (struct sockaddr_in6 *) &port->raddr.addr;
+				dst->sin6_family = AF_INET6;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin6_addr, &ip_addr(address), sizeof(dst->sin6_addr));
+
+				break;
+			}
+			default:
+				elog(ERROR, "unexpected ip_family: %d", ip_family(address));
+				break;
+		}
+	}
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("parameter role is mandatory")));
+	port->user_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
+
+	if (!PG_ARGISNULL(2))
+		ssl_in_use = PG_GETARG_BOOL(2);
+
+	port->ssl_in_use = ssl_in_use;
+
+	tupdesc = CreateTemplateTupleDesc(PG_HBA_MATCHES_ATTS);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "file_name",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "line_num",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "raw_line",
+					   TEXTOID, -1, 0);
+
+	BlessTupleDesc(tupdesc);
+
+	memset(isnull, 0, sizeof(isnull));
+
+	/* FIXME rework API to not rely on PostmasterContext */
+	ctxt = AllocSetContextCreate(CurrentMemoryContext, "load_hba",
+								 ALLOCSET_DEFAULT_SIZES);
+	PostmasterContext = AllocSetContextCreate(ctxt,
+											  "Postmaster",
+											  ALLOCSET_DEFAULT_SIZES);
+	parsed_hba_context = NULL;
+	if (!load_hba())
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("Invalidation auth configuration file")));
+
+	check_hba(port);
+
+	if (port->hba->auth_method == uaImplicitReject)
+		PG_RETURN_NULL();
+
+	values[0] = CStringGetTextDatum(port->hba->sourcefile);
+	values[1] = Int32GetDatum(port->hba->linenumber);
+	values[2] = CStringGetTextDatum(port->hba->rawline);
+
+	MemoryContextDelete(PostmasterContext);
+	PostmasterContext = NULL;
+
+	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index e6a54cc3d6..d78c5a7556 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6139,6 +6139,13 @@
   proargmodes => '{o,o,o,o,o,o,o}',
   proargnames => '{mapping_number,file_name,line_number,map_name,sys_name,pg_username,error}',
   prosrc => 'pg_ident_file_mappings' },
+{ oid => '9557', descr => 'show wether the given connection would match an hba line',
+  proname => 'pg_hba_matches', provolatile => 'v', prorettype => 'record',
+  proargtypes => 'inet text bool', proisstrict => 'f',
+  proallargtypes => '{inet,text,bool,text,int4,text}',
+  proargmodes => '{i,i,i,o,o,o}',
+  proargnames => '{address,role,ssl,file_name,line_num,raw_line}',
+  prosrc => 'pg_hba_matches' },
 { oid => '1371', descr => 'view system lock information',
   proname => 'pg_lock_status', prorows => '1000', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
-- 
2.37.0


--fy3hxyvyv2spu3wq--





^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* [PATCH v9 3/3] POC: Add a pg_hba_matches() function.
@ 2022-02-22 13:34 Julien Rouhaud <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Julien Rouhaud @ 2022-02-22 13:34 UTC (permalink / raw)

Catversion is bumped.

Author: Julien Rouhaud
Reviewed-by: FIXME
Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya%40jrouhaud
---
 src/backend/catalog/system_functions.sql |   9 ++
 src/backend/libpq/hba.c                  | 138 +++++++++++++++++++++++
 src/include/catalog/pg_proc.dat          |   7 ++
 3 files changed, 154 insertions(+)

diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 30a048f6b0..224b7a483a 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -594,6 +594,15 @@ LANGUAGE internal
 STRICT IMMUTABLE PARALLEL SAFE
 AS 'unicode_is_normalized';
 
+CREATE OR REPLACE FUNCTION
+  pg_hba_matches(
+    IN address inet, IN role text, IN ssl bool DEFAULT false,
+    OUT file_name text, OUT line_num int4, OUT raw_line text)
+RETURNS RECORD
+LANGUAGE INTERNAL
+VOLATILE
+AS 'pg_hba_matches';
+
 --
 -- The default permissions for functions mean that anyone can execute them.
 -- A number of functions shouldn't be executable by just anyone, but rather
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 49a8c56f41..a34b315134 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -28,6 +28,7 @@
 #include <unistd.h>
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "common/ip.h"
@@ -43,6 +44,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/inet.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/varlena.h"
@@ -2974,3 +2976,139 @@ hba_authname(UserAuth auth_method)
 
 	return UserAuthName[auth_method];
 }
+
+#define PG_HBA_MATCHES_ATTS	3
+
+/*
+ * SQL-accessible SRF to return the entries that match the given connection
+ * info, if any.
+ */
+Datum pg_hba_matches(PG_FUNCTION_ARGS)
+{
+	MemoryContext ctxt;
+	inet	   *address = NULL;
+	bool		ssl_in_use = false;
+	hbaPort	   *port = palloc0(sizeof(hbaPort));
+	TupleDesc	tupdesc;
+	Datum		values[PG_HBA_MATCHES_ATTS];
+	bool		isnull[PG_HBA_MATCHES_ATTS];
+
+	if (!is_member_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("only superuser or a member of the pg_read_server_files role may call this function")));
+
+	if (PG_ARGISNULL(0))
+		port->raddr.addr.ss_family = AF_UNIX;
+	else
+	{
+		int			bits;
+		char	   *ptr;
+		char		tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+		address = PG_GETARG_INET_PP(0);
+
+		bits = ip_maxbits(address) - ip_bits(address);
+		if (bits != 0)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("Invalid address")));
+		}
+
+		/* force display of max bits, regardless of masklen... */
+		if (pg_inet_net_ntop(ip_family(address), ip_addr(address),
+							 ip_maxbits(address), tmp, sizeof(tmp)) == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+					 errmsg("could not format inet value: %m")));
+
+		/* Suppress /n if present (shouldn't happen now) */
+		if ((ptr = strchr(tmp, '/')) != NULL)
+			*ptr = '\0';
+
+		switch (ip_family(address))
+		{
+			case PGSQL_AF_INET:
+			{
+				struct sockaddr_in *dst;
+
+				dst = (struct sockaddr_in *) &port->raddr.addr;
+				dst->sin_family = AF_INET;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin_addr, &ip_addr(address), sizeof(dst->sin_addr));
+
+				break;
+			}
+			/* See pg_inet_net_ntop() for details about those constants */
+			case PGSQL_AF_INET6:
+#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6
+			case AF_INET6:
+#endif
+			{
+				struct sockaddr_in6 *dst;
+
+				dst = (struct sockaddr_in6 *) &port->raddr.addr;
+				dst->sin6_family = AF_INET6;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin6_addr, &ip_addr(address), sizeof(dst->sin6_addr));
+
+				break;
+			}
+			default:
+				elog(ERROR, "unexpected ip_family: %d", ip_family(address));
+				break;
+		}
+	}
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("parameter role is mandatory")));
+	port->user_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
+
+	if (!PG_ARGISNULL(2))
+		ssl_in_use = PG_GETARG_BOOL(2);
+
+	port->ssl_in_use = ssl_in_use;
+
+	tupdesc = CreateTemplateTupleDesc(PG_HBA_MATCHES_ATTS);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "file_name",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "line_num",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "raw_line",
+					   TEXTOID, -1, 0);
+
+	BlessTupleDesc(tupdesc);
+
+	memset(isnull, 0, sizeof(isnull));
+
+	/* FIXME rework API to not rely on PostmasterContext */
+	ctxt = AllocSetContextCreate(CurrentMemoryContext, "load_hba",
+								 ALLOCSET_DEFAULT_SIZES);
+	PostmasterContext = AllocSetContextCreate(ctxt,
+											  "Postmaster",
+											  ALLOCSET_DEFAULT_SIZES);
+	parsed_hba_context = NULL;
+	if (!load_hba())
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("Invalidation auth configuration file")));
+
+	check_hba(port);
+
+	if (port->hba->auth_method == uaImplicitReject)
+		PG_RETURN_NULL();
+
+	values[0] = CStringGetTextDatum(port->hba->sourcefile);
+	values[1] = Int32GetDatum(port->hba->linenumber);
+	values[2] = CStringGetTextDatum(port->hba->rawline);
+
+	MemoryContextDelete(PostmasterContext);
+	PostmasterContext = NULL;
+
+	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 4470011977..ba632698fe 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6139,6 +6139,13 @@
   proargmodes => '{o,o,o,o,o,o,o}',
   proargnames => '{mapping_number,file_name,line_number,map_name,sys_name,pg_username,error}',
   prosrc => 'pg_ident_file_mappings' },
+{ oid => '9557', descr => 'show wether the given connection would match an hba line',
+  proname => 'pg_hba_matches', provolatile => 'v', prorettype => 'record',
+  proargtypes => 'inet text bool', proisstrict => 'f',
+  proallargtypes => '{inet,text,bool,text,int4,text}',
+  proargmodes => '{i,i,i,o,o,o}',
+  proargnames => '{address,role,ssl,file_name,line_num,raw_line}',
+  prosrc => 'pg_hba_matches' },
 { oid => '1371', descr => 'view system lock information',
   proname => 'pg_lock_status', prorows => '1000', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
-- 
2.37.0


--u2elhxoozhwco244--





^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* [PATCH v12 3/3] POC: Add a pg_hba_matches() function.
@ 2022-02-22 13:34 Julien Rouhaud <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Julien Rouhaud @ 2022-02-22 13:34 UTC (permalink / raw)

Catversion is bumped.

Author: Julien Rouhaud
Reviewed-by: FIXME
Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya%40jrouhaud
---
 src/backend/catalog/system_functions.sql |   9 ++
 src/backend/libpq/hba.c                  | 138 +++++++++++++++++++++++
 src/include/catalog/pg_proc.dat          |   7 ++
 3 files changed, 154 insertions(+)

diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 30a048f6b0..224b7a483a 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -594,6 +594,15 @@ LANGUAGE internal
 STRICT IMMUTABLE PARALLEL SAFE
 AS 'unicode_is_normalized';
 
+CREATE OR REPLACE FUNCTION
+  pg_hba_matches(
+    IN address inet, IN role text, IN ssl bool DEFAULT false,
+    OUT file_name text, OUT line_num int4, OUT raw_line text)
+RETURNS RECORD
+LANGUAGE INTERNAL
+VOLATILE
+AS 'pg_hba_matches';
+
 --
 -- The default permissions for functions mean that anyone can execute them.
 -- A number of functions shouldn't be executable by just anyone, but rather
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 183312b10a..1d64a984a8 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -28,6 +28,7 @@
 #include <unistd.h>
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "common/ip.h"
@@ -43,6 +44,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/inet.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/varlena.h"
@@ -3091,3 +3093,139 @@ hba_authname(UserAuth auth_method)
 
 	return UserAuthName[auth_method];
 }
+
+#define PG_HBA_MATCHES_ATTS	3
+
+/*
+ * SQL-accessible SRF to return the entries that match the given connection
+ * info, if any.
+ */
+Datum pg_hba_matches(PG_FUNCTION_ARGS)
+{
+	MemoryContext ctxt;
+	inet	   *address = NULL;
+	bool		ssl_in_use = false;
+	hbaPort	   *port = palloc0(sizeof(hbaPort));
+	TupleDesc	tupdesc;
+	Datum		values[PG_HBA_MATCHES_ATTS];
+	bool		isnull[PG_HBA_MATCHES_ATTS];
+
+	if (!is_member_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("only superuser or a member of the pg_read_server_files role may call this function")));
+
+	if (PG_ARGISNULL(0))
+		port->raddr.addr.ss_family = AF_UNIX;
+	else
+	{
+		int			bits;
+		char	   *ptr;
+		char		tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+		address = PG_GETARG_INET_PP(0);
+
+		bits = ip_maxbits(address) - ip_bits(address);
+		if (bits != 0)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("Invalid address")));
+		}
+
+		/* force display of max bits, regardless of masklen... */
+		if (pg_inet_net_ntop(ip_family(address), ip_addr(address),
+							 ip_maxbits(address), tmp, sizeof(tmp)) == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+					 errmsg("could not format inet value: %m")));
+
+		/* Suppress /n if present (shouldn't happen now) */
+		if ((ptr = strchr(tmp, '/')) != NULL)
+			*ptr = '\0';
+
+		switch (ip_family(address))
+		{
+			case PGSQL_AF_INET:
+			{
+				struct sockaddr_in *dst;
+
+				dst = (struct sockaddr_in *) &port->raddr.addr;
+				dst->sin_family = AF_INET;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin_addr, &ip_addr(address), sizeof(dst->sin_addr));
+
+				break;
+			}
+			/* See pg_inet_net_ntop() for details about those constants */
+			case PGSQL_AF_INET6:
+#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6
+			case AF_INET6:
+#endif
+			{
+				struct sockaddr_in6 *dst;
+
+				dst = (struct sockaddr_in6 *) &port->raddr.addr;
+				dst->sin6_family = AF_INET6;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin6_addr, &ip_addr(address), sizeof(dst->sin6_addr));
+
+				break;
+			}
+			default:
+				elog(ERROR, "unexpected ip_family: %d", ip_family(address));
+				break;
+		}
+	}
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("parameter role is mandatory")));
+	port->user_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
+
+	if (!PG_ARGISNULL(2))
+		ssl_in_use = PG_GETARG_BOOL(2);
+
+	port->ssl_in_use = ssl_in_use;
+
+	tupdesc = CreateTemplateTupleDesc(PG_HBA_MATCHES_ATTS);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "file_name",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "line_num",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "raw_line",
+					   TEXTOID, -1, 0);
+
+	BlessTupleDesc(tupdesc);
+
+	memset(isnull, 0, sizeof(isnull));
+
+	/* FIXME rework API to not rely on PostmasterContext */
+	ctxt = AllocSetContextCreate(CurrentMemoryContext, "load_hba",
+								 ALLOCSET_DEFAULT_SIZES);
+	PostmasterContext = AllocSetContextCreate(ctxt,
+											  "Postmaster",
+											  ALLOCSET_DEFAULT_SIZES);
+	parsed_hba_context = NULL;
+	if (!load_hba())
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("Invalidation auth configuration file")));
+
+	check_hba(port);
+
+	if (port->hba->auth_method == uaImplicitReject)
+		PG_RETURN_NULL();
+
+	values[0] = CStringGetTextDatum(port->hba->sourcefile);
+	values[1] = Int32GetDatum(port->hba->linenumber);
+	values[2] = CStringGetTextDatum(port->hba->rawline);
+
+	MemoryContextDelete(PostmasterContext);
+	PostmasterContext = NULL;
+
+	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 2ad06c4d3e..60e313696d 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6146,6 +6146,13 @@
   proargmodes => '{o,o,o,o,o,o,o}',
   proargnames => '{mapping_number,file_name,line_number,map_name,sys_name,pg_username,error}',
   prosrc => 'pg_ident_file_mappings' },
+{ oid => '9557', descr => 'show wether the given connection would match an hba line',
+  proname => 'pg_hba_matches', provolatile => 'v', prorettype => 'record',
+  proargtypes => 'inet text bool', proisstrict => 'f',
+  proallargtypes => '{inet,text,bool,text,int4,text}',
+  proargmodes => '{i,i,i,o,o,o}',
+  proargnames => '{address,role,ssl,file_name,line_num,raw_line}',
+  prosrc => 'pg_hba_matches' },
 { oid => '1371', descr => 'view system lock information',
   proname => 'pg_lock_status', prorows => '1000', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
-- 
2.37.0


--mkp7bqke54pr5bne--





^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* [PATCH v6 2/2] POC: Add a pg_hba_matches() function.
@ 2022-02-22 13:34 Julien Rouhaud <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Julien Rouhaud @ 2022-02-22 13:34 UTC (permalink / raw)

Catversion is bumped.

Author: Julien Rouhaud
Reviewed-by: FIXME
Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya%40jrouhaud
---
 src/backend/catalog/system_functions.sql |   9 ++
 src/backend/libpq/hba.c                  | 138 +++++++++++++++++++++++
 src/include/catalog/pg_proc.dat          |   7 ++
 3 files changed, 154 insertions(+)

diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 81bac6f581..049cdabc81 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -594,6 +594,15 @@ LANGUAGE internal
 STRICT IMMUTABLE PARALLEL SAFE
 AS 'unicode_is_normalized';
 
+CREATE OR REPLACE FUNCTION
+  pg_hba_matches(
+    IN address inet, IN role text, IN ssl bool DEFAULT false,
+    OUT file_name text, OUT line_num int4, OUT raw_line text)
+RETURNS RECORD
+LANGUAGE INTERNAL
+VOLATILE
+AS 'pg_hba_matches';
+
 --
 -- The default permissions for functions mean that anyone can execute them.
 -- A number of functions shouldn't be executable by just anyone, but rather
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 5d0caa587b..b44b036d05 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -27,6 +27,7 @@
 #include <unistd.h>
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "common/ip.h"
@@ -42,6 +43,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/inet.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/varlena.h"
@@ -2949,3 +2951,139 @@ hba_authname(UserAuth auth_method)
 
 	return UserAuthName[auth_method];
 }
+
+#define PG_HBA_MATCHES_ATTS	3
+
+/*
+ * SQL-accessible SRF to return the entries that match the given connection
+ * info, if any.
+ */
+Datum pg_hba_matches(PG_FUNCTION_ARGS)
+{
+	MemoryContext ctxt;
+	inet	   *address = NULL;
+	bool		ssl_in_use = false;
+	hbaPort	   *port = palloc0(sizeof(hbaPort));
+	TupleDesc	tupdesc;
+	Datum		values[PG_HBA_MATCHES_ATTS];
+	bool		isnull[PG_HBA_MATCHES_ATTS];
+
+	if (!is_member_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("only superuser or a member of the pg_read_server_files role may call this function")));
+
+	if (PG_ARGISNULL(0))
+		port->raddr.addr.ss_family = AF_UNIX;
+	else
+	{
+		int			bits;
+		char	   *ptr;
+		char		tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+		address = PG_GETARG_INET_PP(0);
+
+		bits = ip_maxbits(address) - ip_bits(address);
+		if (bits != 0)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("Invalid address")));
+		}
+
+		/* force display of max bits, regardless of masklen... */
+		if (pg_inet_net_ntop(ip_family(address), ip_addr(address),
+							 ip_maxbits(address), tmp, sizeof(tmp)) == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+					 errmsg("could not format inet value: %m")));
+
+		/* Suppress /n if present (shouldn't happen now) */
+		if ((ptr = strchr(tmp, '/')) != NULL)
+			*ptr = '\0';
+
+		switch (ip_family(address))
+		{
+			case PGSQL_AF_INET:
+			{
+				struct sockaddr_in *dst;
+
+				dst = (struct sockaddr_in *) &port->raddr.addr;
+				dst->sin_family = AF_INET;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin_addr, &ip_addr(address), sizeof(dst->sin_addr));
+
+				break;
+			}
+			/* See pg_inet_net_ntop() for details about those constants */
+			case PGSQL_AF_INET6:
+#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6
+			case AF_INET6:
+#endif
+			{
+				struct sockaddr_in6 *dst;
+
+				dst = (struct sockaddr_in6 *) &port->raddr.addr;
+				dst->sin6_family = AF_INET6;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin6_addr, &ip_addr(address), sizeof(dst->sin6_addr));
+
+				break;
+			}
+			default:
+				elog(ERROR, "unexpected ip_family: %d", ip_family(address));
+				break;
+		}
+	}
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("parameter role is mandatory")));
+	port->user_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
+
+	if (!PG_ARGISNULL(2))
+		ssl_in_use = PG_GETARG_BOOL(2);
+
+	port->ssl_in_use = ssl_in_use;
+
+	tupdesc = CreateTemplateTupleDesc(PG_HBA_MATCHES_ATTS);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "file_name",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "line_num",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "raw_line",
+					   TEXTOID, -1, 0);
+
+	BlessTupleDesc(tupdesc);
+
+	memset(isnull, 0, sizeof(isnull));
+
+	/* FIXME rework API to not rely on PostmasterContext */
+	ctxt = AllocSetContextCreate(CurrentMemoryContext, "load_hba",
+								 ALLOCSET_DEFAULT_SIZES);
+	PostmasterContext = AllocSetContextCreate(ctxt,
+											  "Postmaster",
+											  ALLOCSET_DEFAULT_SIZES);
+	parsed_hba_context = NULL;
+	if (!load_hba())
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("Invalidation auth configuration file")));
+
+	check_hba(port);
+
+	if (port->hba->auth_method == uaImplicitReject)
+		PG_RETURN_NULL();
+
+	values[0] = CStringGetTextDatum(port->hba->sourcefile);
+	values[1] = Int32GetDatum(port->hba->linenumber);
+	values[2] = CStringGetTextDatum(port->hba->rawline);
+
+	MemoryContextDelete(PostmasterContext);
+	PostmasterContext = NULL;
+
+	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 467f6f1293..2b6fa8aeba 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6122,6 +6122,13 @@
   proargmodes => '{o,o,o,o,o,o,o}',
   proargnames => '{mapping_number,file_name,line_number,map_name,sys_name,pg_username,error}',
   prosrc => 'pg_ident_file_mappings' },
+{ oid => '9557', descr => 'show wether the given connection would match an hba line',
+  proname => 'pg_hba_matches', provolatile => 'v', prorettype => 'record',
+  proargtypes => 'inet text bool', proisstrict => 'f',
+  proallargtypes => '{inet,text,bool,text,int4,text}',
+  proargmodes => '{i,i,i,o,o,o}',
+  proargnames => '{address,role,ssl,file_name,line_num,raw_line}',
+  prosrc => 'pg_hba_matches' },
 { oid => '1371', descr => 'view system lock information',
   proname => 'pg_lock_status', prorows => '1000', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
-- 
2.33.1


--g2rofdrijxvb5udh--





^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* [PATCH v7 2/2] POC: Add a pg_hba_matches() function.
@ 2022-02-22 13:34 Julien Rouhaud <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Julien Rouhaud @ 2022-02-22 13:34 UTC (permalink / raw)

Catversion is bumped.

Author: Julien Rouhaud
Reviewed-by: FIXME
Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya%40jrouhaud
---
 src/backend/catalog/system_functions.sql |   9 ++
 src/backend/libpq/hba.c                  | 138 +++++++++++++++++++++++
 src/include/catalog/pg_proc.dat          |   7 ++
 3 files changed, 154 insertions(+)

diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 73da687d5d..bfee72f705 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -594,6 +594,15 @@ LANGUAGE internal
 STRICT IMMUTABLE PARALLEL SAFE
 AS 'unicode_is_normalized';
 
+CREATE OR REPLACE FUNCTION
+  pg_hba_matches(
+    IN address inet, IN role text, IN ssl bool DEFAULT false,
+    OUT file_name text, OUT line_num int4, OUT raw_line text)
+RETURNS RECORD
+LANGUAGE INTERNAL
+VOLATILE
+AS 'pg_hba_matches';
+
 --
 -- The default permissions for functions mean that anyone can execute them.
 -- A number of functions shouldn't be executable by just anyone, but rather
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 56b6cec9d5..09554a9bc6 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -27,6 +27,7 @@
 #include <unistd.h>
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "common/ip.h"
@@ -42,6 +43,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/inet.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/varlena.h"
@@ -2946,3 +2948,139 @@ hba_authname(UserAuth auth_method)
 
 	return UserAuthName[auth_method];
 }
+
+#define PG_HBA_MATCHES_ATTS	3
+
+/*
+ * SQL-accessible SRF to return the entries that match the given connection
+ * info, if any.
+ */
+Datum pg_hba_matches(PG_FUNCTION_ARGS)
+{
+	MemoryContext ctxt;
+	inet	   *address = NULL;
+	bool		ssl_in_use = false;
+	hbaPort	   *port = palloc0(sizeof(hbaPort));
+	TupleDesc	tupdesc;
+	Datum		values[PG_HBA_MATCHES_ATTS];
+	bool		isnull[PG_HBA_MATCHES_ATTS];
+
+	if (!is_member_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("only superuser or a member of the pg_read_server_files role may call this function")));
+
+	if (PG_ARGISNULL(0))
+		port->raddr.addr.ss_family = AF_UNIX;
+	else
+	{
+		int			bits;
+		char	   *ptr;
+		char		tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+		address = PG_GETARG_INET_PP(0);
+
+		bits = ip_maxbits(address) - ip_bits(address);
+		if (bits != 0)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("Invalid address")));
+		}
+
+		/* force display of max bits, regardless of masklen... */
+		if (pg_inet_net_ntop(ip_family(address), ip_addr(address),
+							 ip_maxbits(address), tmp, sizeof(tmp)) == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+					 errmsg("could not format inet value: %m")));
+
+		/* Suppress /n if present (shouldn't happen now) */
+		if ((ptr = strchr(tmp, '/')) != NULL)
+			*ptr = '\0';
+
+		switch (ip_family(address))
+		{
+			case PGSQL_AF_INET:
+			{
+				struct sockaddr_in *dst;
+
+				dst = (struct sockaddr_in *) &port->raddr.addr;
+				dst->sin_family = AF_INET;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin_addr, &ip_addr(address), sizeof(dst->sin_addr));
+
+				break;
+			}
+			/* See pg_inet_net_ntop() for details about those constants */
+			case PGSQL_AF_INET6:
+#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6
+			case AF_INET6:
+#endif
+			{
+				struct sockaddr_in6 *dst;
+
+				dst = (struct sockaddr_in6 *) &port->raddr.addr;
+				dst->sin6_family = AF_INET6;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin6_addr, &ip_addr(address), sizeof(dst->sin6_addr));
+
+				break;
+			}
+			default:
+				elog(ERROR, "unexpected ip_family: %d", ip_family(address));
+				break;
+		}
+	}
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("parameter role is mandatory")));
+	port->user_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
+
+	if (!PG_ARGISNULL(2))
+		ssl_in_use = PG_GETARG_BOOL(2);
+
+	port->ssl_in_use = ssl_in_use;
+
+	tupdesc = CreateTemplateTupleDesc(PG_HBA_MATCHES_ATTS);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "file_name",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "line_num",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "raw_line",
+					   TEXTOID, -1, 0);
+
+	BlessTupleDesc(tupdesc);
+
+	memset(isnull, 0, sizeof(isnull));
+
+	/* FIXME rework API to not rely on PostmasterContext */
+	ctxt = AllocSetContextCreate(CurrentMemoryContext, "load_hba",
+								 ALLOCSET_DEFAULT_SIZES);
+	PostmasterContext = AllocSetContextCreate(ctxt,
+											  "Postmaster",
+											  ALLOCSET_DEFAULT_SIZES);
+	parsed_hba_context = NULL;
+	if (!load_hba())
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("Invalidation auth configuration file")));
+
+	check_hba(port);
+
+	if (port->hba->auth_method == uaImplicitReject)
+		PG_RETURN_NULL();
+
+	values[0] = CStringGetTextDatum(port->hba->sourcefile);
+	values[1] = Int32GetDatum(port->hba->linenumber);
+	values[2] = CStringGetTextDatum(port->hba->rawline);
+
+	MemoryContextDelete(PostmasterContext);
+	PostmasterContext = NULL;
+
+	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 65bfd32753..2c65ee2019 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6139,6 +6139,13 @@
   proargmodes => '{o,o,o,o,o,o,o}',
   proargnames => '{mapping_number,file_name,line_number,map_name,sys_name,pg_username,error}',
   prosrc => 'pg_ident_file_mappings' },
+{ oid => '9557', descr => 'show wether the given connection would match an hba line',
+  proname => 'pg_hba_matches', provolatile => 'v', prorettype => 'record',
+  proargtypes => 'inet text bool', proisstrict => 'f',
+  proallargtypes => '{inet,text,bool,text,int4,text}',
+  proargmodes => '{i,i,i,o,o,o}',
+  proargnames => '{address,role,ssl,file_name,line_num,raw_line}',
+  prosrc => 'pg_hba_matches' },
 { oid => '1371', descr => 'view system lock information',
   proname => 'pg_lock_status', prorows => '1000', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
-- 
2.33.1


--o45jk7yo2ulu7ifa--





^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* [PATCH v11 3/3] POC: Add a pg_hba_matches() function.
@ 2022-02-22 13:34 Julien Rouhaud <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Julien Rouhaud @ 2022-02-22 13:34 UTC (permalink / raw)

Catversion is bumped.

Author: Julien Rouhaud
Reviewed-by: FIXME
Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya%40jrouhaud
---
 src/backend/catalog/system_functions.sql |   9 ++
 src/backend/libpq/hba.c                  | 138 +++++++++++++++++++++++
 src/include/catalog/pg_proc.dat          |   7 ++
 3 files changed, 154 insertions(+)

diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 30a048f6b0..224b7a483a 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -594,6 +594,15 @@ LANGUAGE internal
 STRICT IMMUTABLE PARALLEL SAFE
 AS 'unicode_is_normalized';
 
+CREATE OR REPLACE FUNCTION
+  pg_hba_matches(
+    IN address inet, IN role text, IN ssl bool DEFAULT false,
+    OUT file_name text, OUT line_num int4, OUT raw_line text)
+RETURNS RECORD
+LANGUAGE INTERNAL
+VOLATILE
+AS 'pg_hba_matches';
+
 --
 -- The default permissions for functions mean that anyone can execute them.
 -- A number of functions shouldn't be executable by just anyone, but rather
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 4e4a45b793..c858f4188d 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -28,6 +28,7 @@
 #include <unistd.h>
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "common/ip.h"
@@ -43,6 +44,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/inet.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/varlena.h"
@@ -2967,3 +2969,139 @@ hba_authname(UserAuth auth_method)
 
 	return UserAuthName[auth_method];
 }
+
+#define PG_HBA_MATCHES_ATTS	3
+
+/*
+ * SQL-accessible SRF to return the entries that match the given connection
+ * info, if any.
+ */
+Datum pg_hba_matches(PG_FUNCTION_ARGS)
+{
+	MemoryContext ctxt;
+	inet	   *address = NULL;
+	bool		ssl_in_use = false;
+	hbaPort	   *port = palloc0(sizeof(hbaPort));
+	TupleDesc	tupdesc;
+	Datum		values[PG_HBA_MATCHES_ATTS];
+	bool		isnull[PG_HBA_MATCHES_ATTS];
+
+	if (!is_member_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("only superuser or a member of the pg_read_server_files role may call this function")));
+
+	if (PG_ARGISNULL(0))
+		port->raddr.addr.ss_family = AF_UNIX;
+	else
+	{
+		int			bits;
+		char	   *ptr;
+		char		tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+		address = PG_GETARG_INET_PP(0);
+
+		bits = ip_maxbits(address) - ip_bits(address);
+		if (bits != 0)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("Invalid address")));
+		}
+
+		/* force display of max bits, regardless of masklen... */
+		if (pg_inet_net_ntop(ip_family(address), ip_addr(address),
+							 ip_maxbits(address), tmp, sizeof(tmp)) == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+					 errmsg("could not format inet value: %m")));
+
+		/* Suppress /n if present (shouldn't happen now) */
+		if ((ptr = strchr(tmp, '/')) != NULL)
+			*ptr = '\0';
+
+		switch (ip_family(address))
+		{
+			case PGSQL_AF_INET:
+			{
+				struct sockaddr_in *dst;
+
+				dst = (struct sockaddr_in *) &port->raddr.addr;
+				dst->sin_family = AF_INET;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin_addr, &ip_addr(address), sizeof(dst->sin_addr));
+
+				break;
+			}
+			/* See pg_inet_net_ntop() for details about those constants */
+			case PGSQL_AF_INET6:
+#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6
+			case AF_INET6:
+#endif
+			{
+				struct sockaddr_in6 *dst;
+
+				dst = (struct sockaddr_in6 *) &port->raddr.addr;
+				dst->sin6_family = AF_INET6;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin6_addr, &ip_addr(address), sizeof(dst->sin6_addr));
+
+				break;
+			}
+			default:
+				elog(ERROR, "unexpected ip_family: %d", ip_family(address));
+				break;
+		}
+	}
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("parameter role is mandatory")));
+	port->user_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
+
+	if (!PG_ARGISNULL(2))
+		ssl_in_use = PG_GETARG_BOOL(2);
+
+	port->ssl_in_use = ssl_in_use;
+
+	tupdesc = CreateTemplateTupleDesc(PG_HBA_MATCHES_ATTS);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "file_name",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "line_num",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "raw_line",
+					   TEXTOID, -1, 0);
+
+	BlessTupleDesc(tupdesc);
+
+	memset(isnull, 0, sizeof(isnull));
+
+	/* FIXME rework API to not rely on PostmasterContext */
+	ctxt = AllocSetContextCreate(CurrentMemoryContext, "load_hba",
+								 ALLOCSET_DEFAULT_SIZES);
+	PostmasterContext = AllocSetContextCreate(ctxt,
+											  "Postmaster",
+											  ALLOCSET_DEFAULT_SIZES);
+	parsed_hba_context = NULL;
+	if (!load_hba())
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("Invalidation auth configuration file")));
+
+	check_hba(port);
+
+	if (port->hba->auth_method == uaImplicitReject)
+		PG_RETURN_NULL();
+
+	values[0] = CStringGetTextDatum(port->hba->sourcefile);
+	values[1] = Int32GetDatum(port->hba->linenumber);
+	values[2] = CStringGetTextDatum(port->hba->rawline);
+
+	MemoryContextDelete(PostmasterContext);
+	PostmasterContext = NULL;
+
+	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 807fe3890b..6274585c26 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6142,6 +6142,13 @@
   proargmodes => '{o,o,o,o,o,o,o}',
   proargnames => '{mapping_number,file_name,line_number,map_name,sys_name,pg_username,error}',
   prosrc => 'pg_ident_file_mappings' },
+{ oid => '9557', descr => 'show wether the given connection would match an hba line',
+  proname => 'pg_hba_matches', provolatile => 'v', prorettype => 'record',
+  proargtypes => 'inet text bool', proisstrict => 'f',
+  proallargtypes => '{inet,text,bool,text,int4,text}',
+  proargmodes => '{i,i,i,o,o,o}',
+  proargnames => '{address,role,ssl,file_name,line_num,raw_line}',
+  prosrc => 'pg_hba_matches' },
 { oid => '1371', descr => 'view system lock information',
   proname => 'pg_lock_status', prorows => '1000', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
-- 
2.37.0


--z5mbrmnmd43fw5pt--





^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* [PATCH v2 4/4] POC: Add a pg_hba_matches() function.
@ 2022-02-22 13:34 Julien Rouhaud <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Julien Rouhaud @ 2022-02-22 13:34 UTC (permalink / raw)

Author: Julien Rouhaud
Reviewed-by: FIXME
Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya%40jrouhaud
---
 src/backend/catalog/system_functions.sql |   9 ++
 src/backend/libpq/hba.c                  | 138 +++++++++++++++++++++++
 src/include/catalog/pg_proc.dat          |   7 ++
 3 files changed, 154 insertions(+)

diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index fd1421788e..ae839a4b76 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -594,6 +594,15 @@ LANGUAGE internal
 STRICT IMMUTABLE PARALLEL SAFE
 AS 'unicode_is_normalized';
 
+CREATE OR REPLACE FUNCTION
+  pg_hba_matches(
+    IN address inet, IN role text, IN ssl bool DEFAULT false,
+    OUT file_name text, OUT line_num int4, OUT raw_line text)
+RETURNS RECORD
+LANGUAGE INTERNAL
+VOLATILE
+AS 'pg_hba_matches';
+
 --
 -- The default permissions for functions mean that anyone can execute them.
 -- A number of functions shouldn't be executable by just anyone, but rather
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 85988fdeda..e3e4a0ce7d 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -26,6 +26,7 @@
 #include <unistd.h>
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "common/ip.h"
@@ -41,6 +42,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/inet.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/varlena.h"
@@ -2838,3 +2840,139 @@ tokenize_auth_file(const char *filename, FILE *file, List **tok_lines,
 
 	return linecxt;
 }
+
+#define PG_HBA_MATCHES_ATTS	3
+
+/*
+ * SQL-accessible SRF to return the entries that match the given connection
+ * info, if any.
+ */
+Datum pg_hba_matches(PG_FUNCTION_ARGS)
+{
+	MemoryContext ctxt;
+	inet	   *address = NULL;
+	bool		ssl_in_use = false;
+	hbaPort	   *port = palloc0(sizeof(hbaPort));
+	TupleDesc	tupdesc;
+	Datum		values[PG_HBA_MATCHES_ATTS];
+	bool		isnull[PG_HBA_MATCHES_ATTS];
+
+	if (!is_member_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("only superuser or a member of the pg_read_server_files role may call this function")));
+
+	if (PG_ARGISNULL(0))
+		port->raddr.addr.ss_family = AF_UNIX;
+	else
+	{
+		int			bits;
+		char	   *ptr;
+		char		tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+		address = PG_GETARG_INET_PP(0);
+
+		bits = ip_maxbits(address) - ip_bits(address);
+		if (bits != 0)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("Invalid address")));
+		}
+
+		/* force display of max bits, regardless of masklen... */
+		if (pg_inet_net_ntop(ip_family(address), ip_addr(address),
+							 ip_maxbits(address), tmp, sizeof(tmp)) == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+					 errmsg("could not format inet value: %m")));
+
+		/* Suppress /n if present (shouldn't happen now) */
+		if ((ptr = strchr(tmp, '/')) != NULL)
+			*ptr = '\0';
+
+		switch (ip_family(address))
+		{
+			case PGSQL_AF_INET:
+			{
+				struct sockaddr_in *dst;
+
+				dst = (struct sockaddr_in *) &port->raddr.addr;
+				dst->sin_family = AF_INET;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin_addr, &ip_addr(address), sizeof(dst->sin_addr));
+
+				break;
+			}
+			/* See pg_inet_net_ntop() for details about those constants */
+			case PGSQL_AF_INET6:
+#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6
+			case AF_INET6:
+#endif
+			{
+				struct sockaddr_in6 *dst;
+
+				dst = (struct sockaddr_in6 *) &port->raddr.addr;
+				dst->sin6_family = AF_INET6;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin6_addr, &ip_addr(address), sizeof(dst->sin6_addr));
+
+				break;
+			}
+			default:
+				elog(ERROR, "unexpected ip_family: %d", ip_family(address));
+				break;
+		}
+	}
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("parameter role is mandatory")));
+	port->user_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
+
+	if (!PG_ARGISNULL(2))
+		ssl_in_use = PG_GETARG_BOOL(2);
+
+	port->ssl_in_use = ssl_in_use;
+
+	tupdesc = CreateTemplateTupleDesc(PG_HBA_MATCHES_ATTS);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "file_name",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "line_num",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "raw_line",
+					   TEXTOID, -1, 0);
+
+	BlessTupleDesc(tupdesc);
+
+	memset(isnull, 0, sizeof(isnull));
+
+	/* FIXME rework API to not rely on PostmasterContext */
+	ctxt = AllocSetContextCreate(CurrentMemoryContext, "load_hba",
+								 ALLOCSET_DEFAULT_SIZES);
+	PostmasterContext = AllocSetContextCreate(ctxt,
+											  "Postmaster",
+											  ALLOCSET_DEFAULT_SIZES);
+	parsed_hba_context = NULL;
+	if (!load_hba())
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("Invalidation auth configuration file")));
+
+	check_hba(port);
+
+	if (port->hba->auth_method == uaImplicitReject)
+		PG_RETURN_NULL();
+
+	values[0] = CStringGetTextDatum(port->hba->sourcefile);
+	values[1] = Int32GetDatum(port->hba->linenumber);
+	values[2] = CStringGetTextDatum(port->hba->rawline);
+
+	MemoryContextDelete(PostmasterContext);
+	PostmasterContext = NULL;
+
+	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index e3baafc3e3..a8577a9948 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6127,6 +6127,13 @@
   proargmodes => '{o,o,o,o,o,o,o}',
   proargnames => '{mapping_number,file_name,line_number,map_name,sys_name,pg_usernamee,error}',
   prosrc => 'pg_ident_file_mappings' },
+{ oid => '9557', descr => 'show wether the given connection would match an hba line',
+  proname => 'pg_hba_matches', provolatile => 'v', prorettype => 'record',
+  proargtypes => 'inet text bool', proisstrict => 'f',
+  proallargtypes => '{inet,text,bool,text,int4,text}',
+  proargmodes => '{i,i,i,o,o,o}',
+  proargnames => '{address,role,ssl,file_name,line_num,raw_line}',
+  prosrc => 'pg_hba_matches' },
 { oid => '1371', descr => 'view system lock information',
   proname => 'pg_lock_status', prorows => '1000', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
-- 
2.33.1


--uqjaptwdgaq4xou6--





^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* [PATCH v3 4/4] POC: Add a pg_hba_matches() function.
@ 2022-02-22 13:34 Julien Rouhaud <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Julien Rouhaud @ 2022-02-22 13:34 UTC (permalink / raw)

Author: Julien Rouhaud
Reviewed-by: FIXME
Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya%40jrouhaud
---
 src/backend/catalog/system_functions.sql |   9 ++
 src/backend/libpq/hba.c                  | 138 +++++++++++++++++++++++
 src/include/catalog/pg_proc.dat          |   7 ++
 3 files changed, 154 insertions(+)

diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 81bac6f581..049cdabc81 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -594,6 +594,15 @@ LANGUAGE internal
 STRICT IMMUTABLE PARALLEL SAFE
 AS 'unicode_is_normalized';
 
+CREATE OR REPLACE FUNCTION
+  pg_hba_matches(
+    IN address inet, IN role text, IN ssl bool DEFAULT false,
+    OUT file_name text, OUT line_num int4, OUT raw_line text)
+RETURNS RECORD
+LANGUAGE INTERNAL
+VOLATILE
+AS 'pg_hba_matches';
+
 --
 -- The default permissions for functions mean that anyone can execute them.
 -- A number of functions shouldn't be executable by just anyone, but rather
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 1bfcaef025..8afbb16b76 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -26,6 +26,7 @@
 #include <unistd.h>
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "common/ip.h"
@@ -41,6 +42,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/inet.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/varlena.h"
@@ -2833,3 +2835,139 @@ hba_authname(UserAuth auth_method)
 
 	return UserAuthName[auth_method];
 }
+
+#define PG_HBA_MATCHES_ATTS	3
+
+/*
+ * SQL-accessible SRF to return the entries that match the given connection
+ * info, if any.
+ */
+Datum pg_hba_matches(PG_FUNCTION_ARGS)
+{
+	MemoryContext ctxt;
+	inet	   *address = NULL;
+	bool		ssl_in_use = false;
+	hbaPort	   *port = palloc0(sizeof(hbaPort));
+	TupleDesc	tupdesc;
+	Datum		values[PG_HBA_MATCHES_ATTS];
+	bool		isnull[PG_HBA_MATCHES_ATTS];
+
+	if (!is_member_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("only superuser or a member of the pg_read_server_files role may call this function")));
+
+	if (PG_ARGISNULL(0))
+		port->raddr.addr.ss_family = AF_UNIX;
+	else
+	{
+		int			bits;
+		char	   *ptr;
+		char		tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+		address = PG_GETARG_INET_PP(0);
+
+		bits = ip_maxbits(address) - ip_bits(address);
+		if (bits != 0)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("Invalid address")));
+		}
+
+		/* force display of max bits, regardless of masklen... */
+		if (pg_inet_net_ntop(ip_family(address), ip_addr(address),
+							 ip_maxbits(address), tmp, sizeof(tmp)) == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+					 errmsg("could not format inet value: %m")));
+
+		/* Suppress /n if present (shouldn't happen now) */
+		if ((ptr = strchr(tmp, '/')) != NULL)
+			*ptr = '\0';
+
+		switch (ip_family(address))
+		{
+			case PGSQL_AF_INET:
+			{
+				struct sockaddr_in *dst;
+
+				dst = (struct sockaddr_in *) &port->raddr.addr;
+				dst->sin_family = AF_INET;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin_addr, &ip_addr(address), sizeof(dst->sin_addr));
+
+				break;
+			}
+			/* See pg_inet_net_ntop() for details about those constants */
+			case PGSQL_AF_INET6:
+#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6
+			case AF_INET6:
+#endif
+			{
+				struct sockaddr_in6 *dst;
+
+				dst = (struct sockaddr_in6 *) &port->raddr.addr;
+				dst->sin6_family = AF_INET6;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin6_addr, &ip_addr(address), sizeof(dst->sin6_addr));
+
+				break;
+			}
+			default:
+				elog(ERROR, "unexpected ip_family: %d", ip_family(address));
+				break;
+		}
+	}
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("parameter role is mandatory")));
+	port->user_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
+
+	if (!PG_ARGISNULL(2))
+		ssl_in_use = PG_GETARG_BOOL(2);
+
+	port->ssl_in_use = ssl_in_use;
+
+	tupdesc = CreateTemplateTupleDesc(PG_HBA_MATCHES_ATTS);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "file_name",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "line_num",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "raw_line",
+					   TEXTOID, -1, 0);
+
+	BlessTupleDesc(tupdesc);
+
+	memset(isnull, 0, sizeof(isnull));
+
+	/* FIXME rework API to not rely on PostmasterContext */
+	ctxt = AllocSetContextCreate(CurrentMemoryContext, "load_hba",
+								 ALLOCSET_DEFAULT_SIZES);
+	PostmasterContext = AllocSetContextCreate(ctxt,
+											  "Postmaster",
+											  ALLOCSET_DEFAULT_SIZES);
+	parsed_hba_context = NULL;
+	if (!load_hba())
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("Invalidation auth configuration file")));
+
+	check_hba(port);
+
+	if (port->hba->auth_method == uaImplicitReject)
+		PG_RETURN_NULL();
+
+	values[0] = CStringGetTextDatum(port->hba->sourcefile);
+	values[1] = Int32GetDatum(port->hba->linenumber);
+	values[2] = CStringGetTextDatum(port->hba->rawline);
+
+	MemoryContextDelete(PostmasterContext);
+	PostmasterContext = NULL;
+
+	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 0e8a589302..ee77bd1136 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6122,6 +6122,13 @@
   proargmodes => '{o,o,o,o,o,o,o}',
   proargnames => '{mapping_number,file_name,line_number,map_name,sys_name,pg_usernamee,error}',
   prosrc => 'pg_ident_file_mappings' },
+{ oid => '9557', descr => 'show wether the given connection would match an hba line',
+  proname => 'pg_hba_matches', provolatile => 'v', prorettype => 'record',
+  proargtypes => 'inet text bool', proisstrict => 'f',
+  proallargtypes => '{inet,text,bool,text,int4,text}',
+  proargmodes => '{i,i,i,o,o,o}',
+  proargnames => '{address,role,ssl,file_name,line_num,raw_line}',
+  prosrc => 'pg_hba_matches' },
 { oid => '1371', descr => 'view system lock information',
   proname => 'pg_lock_status', prorows => '1000', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
-- 
2.33.1


--45a4to7ul53bhaf6--





^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* [PATCH v4 3/3] POC: Add a pg_hba_matches() function.
@ 2022-02-22 13:34 Julien Rouhaud <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Julien Rouhaud @ 2022-02-22 13:34 UTC (permalink / raw)

Catversion is bumped.

Author: Julien Rouhaud
Reviewed-by: FIXME
Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya%40jrouhaud
---
 src/backend/catalog/system_functions.sql |   9 ++
 src/backend/libpq/hba.c                  | 138 +++++++++++++++++++++++
 src/include/catalog/pg_proc.dat          |   7 ++
 3 files changed, 154 insertions(+)

diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 81bac6f581..049cdabc81 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -594,6 +594,15 @@ LANGUAGE internal
 STRICT IMMUTABLE PARALLEL SAFE
 AS 'unicode_is_normalized';
 
+CREATE OR REPLACE FUNCTION
+  pg_hba_matches(
+    IN address inet, IN role text, IN ssl bool DEFAULT false,
+    OUT file_name text, OUT line_num int4, OUT raw_line text)
+RETURNS RECORD
+LANGUAGE INTERNAL
+VOLATILE
+AS 'pg_hba_matches';
+
 --
 -- The default permissions for functions mean that anyone can execute them.
 -- A number of functions shouldn't be executable by just anyone, but rather
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 1551b34c53..a757a54c87 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -26,6 +26,7 @@
 #include <unistd.h>
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "common/ip.h"
@@ -41,6 +42,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/inet.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/varlena.h"
@@ -2835,3 +2837,139 @@ hba_authname(UserAuth auth_method)
 
 	return UserAuthName[auth_method];
 }
+
+#define PG_HBA_MATCHES_ATTS	3
+
+/*
+ * SQL-accessible SRF to return the entries that match the given connection
+ * info, if any.
+ */
+Datum pg_hba_matches(PG_FUNCTION_ARGS)
+{
+	MemoryContext ctxt;
+	inet	   *address = NULL;
+	bool		ssl_in_use = false;
+	hbaPort	   *port = palloc0(sizeof(hbaPort));
+	TupleDesc	tupdesc;
+	Datum		values[PG_HBA_MATCHES_ATTS];
+	bool		isnull[PG_HBA_MATCHES_ATTS];
+
+	if (!is_member_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("only superuser or a member of the pg_read_server_files role may call this function")));
+
+	if (PG_ARGISNULL(0))
+		port->raddr.addr.ss_family = AF_UNIX;
+	else
+	{
+		int			bits;
+		char	   *ptr;
+		char		tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+		address = PG_GETARG_INET_PP(0);
+
+		bits = ip_maxbits(address) - ip_bits(address);
+		if (bits != 0)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("Invalid address")));
+		}
+
+		/* force display of max bits, regardless of masklen... */
+		if (pg_inet_net_ntop(ip_family(address), ip_addr(address),
+							 ip_maxbits(address), tmp, sizeof(tmp)) == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+					 errmsg("could not format inet value: %m")));
+
+		/* Suppress /n if present (shouldn't happen now) */
+		if ((ptr = strchr(tmp, '/')) != NULL)
+			*ptr = '\0';
+
+		switch (ip_family(address))
+		{
+			case PGSQL_AF_INET:
+			{
+				struct sockaddr_in *dst;
+
+				dst = (struct sockaddr_in *) &port->raddr.addr;
+				dst->sin_family = AF_INET;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin_addr, &ip_addr(address), sizeof(dst->sin_addr));
+
+				break;
+			}
+			/* See pg_inet_net_ntop() for details about those constants */
+			case PGSQL_AF_INET6:
+#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6
+			case AF_INET6:
+#endif
+			{
+				struct sockaddr_in6 *dst;
+
+				dst = (struct sockaddr_in6 *) &port->raddr.addr;
+				dst->sin6_family = AF_INET6;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin6_addr, &ip_addr(address), sizeof(dst->sin6_addr));
+
+				break;
+			}
+			default:
+				elog(ERROR, "unexpected ip_family: %d", ip_family(address));
+				break;
+		}
+	}
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("parameter role is mandatory")));
+	port->user_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
+
+	if (!PG_ARGISNULL(2))
+		ssl_in_use = PG_GETARG_BOOL(2);
+
+	port->ssl_in_use = ssl_in_use;
+
+	tupdesc = CreateTemplateTupleDesc(PG_HBA_MATCHES_ATTS);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "file_name",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "line_num",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "raw_line",
+					   TEXTOID, -1, 0);
+
+	BlessTupleDesc(tupdesc);
+
+	memset(isnull, 0, sizeof(isnull));
+
+	/* FIXME rework API to not rely on PostmasterContext */
+	ctxt = AllocSetContextCreate(CurrentMemoryContext, "load_hba",
+								 ALLOCSET_DEFAULT_SIZES);
+	PostmasterContext = AllocSetContextCreate(ctxt,
+											  "Postmaster",
+											  ALLOCSET_DEFAULT_SIZES);
+	parsed_hba_context = NULL;
+	if (!load_hba())
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("Invalidation auth configuration file")));
+
+	check_hba(port);
+
+	if (port->hba->auth_method == uaImplicitReject)
+		PG_RETURN_NULL();
+
+	values[0] = CStringGetTextDatum(port->hba->sourcefile);
+	values[1] = Int32GetDatum(port->hba->linenumber);
+	values[2] = CStringGetTextDatum(port->hba->rawline);
+
+	MemoryContextDelete(PostmasterContext);
+	PostmasterContext = NULL;
+
+	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 87842d5ce2..3a9c1261dc 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6122,6 +6122,13 @@
   proargmodes => '{o,o,o,o,o,o,o}',
   proargnames => '{mapping_number,file_name,line_number,map_name,sys_name,pg_username,error}',
   prosrc => 'pg_ident_file_mappings' },
+{ oid => '9557', descr => 'show wether the given connection would match an hba line',
+  proname => 'pg_hba_matches', provolatile => 'v', prorettype => 'record',
+  proargtypes => 'inet text bool', proisstrict => 'f',
+  proallargtypes => '{inet,text,bool,text,int4,text}',
+  proargmodes => '{i,i,i,o,o,o}',
+  proargnames => '{address,role,ssl,file_name,line_num,raw_line}',
+  prosrc => 'pg_hba_matches' },
 { oid => '1371', descr => 'view system lock information',
   proname => 'pg_lock_status', prorows => '1000', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
-- 
2.33.1


--4xmgvl4uv4ppkth6--





^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* [PATCH v5 3/3] POC: Add a pg_hba_matches() function.
@ 2022-02-22 13:34 Julien Rouhaud <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Julien Rouhaud @ 2022-02-22 13:34 UTC (permalink / raw)

Catversion is bumped.

Author: Julien Rouhaud
Reviewed-by: FIXME
Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya%40jrouhaud
---
 src/backend/catalog/system_functions.sql |   9 ++
 src/backend/libpq/hba.c                  | 138 +++++++++++++++++++++++
 src/include/catalog/pg_proc.dat          |   7 ++
 3 files changed, 154 insertions(+)

diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 81bac6f581..049cdabc81 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -594,6 +594,15 @@ LANGUAGE internal
 STRICT IMMUTABLE PARALLEL SAFE
 AS 'unicode_is_normalized';
 
+CREATE OR REPLACE FUNCTION
+  pg_hba_matches(
+    IN address inet, IN role text, IN ssl bool DEFAULT false,
+    OUT file_name text, OUT line_num int4, OUT raw_line text)
+RETURNS RECORD
+LANGUAGE INTERNAL
+VOLATILE
+AS 'pg_hba_matches';
+
 --
 -- The default permissions for functions mean that anyone can execute them.
 -- A number of functions shouldn't be executable by just anyone, but rather
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 1551b34c53..a757a54c87 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -26,6 +26,7 @@
 #include <unistd.h>
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "common/ip.h"
@@ -41,6 +42,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/inet.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/varlena.h"
@@ -2835,3 +2837,139 @@ hba_authname(UserAuth auth_method)
 
 	return UserAuthName[auth_method];
 }
+
+#define PG_HBA_MATCHES_ATTS	3
+
+/*
+ * SQL-accessible SRF to return the entries that match the given connection
+ * info, if any.
+ */
+Datum pg_hba_matches(PG_FUNCTION_ARGS)
+{
+	MemoryContext ctxt;
+	inet	   *address = NULL;
+	bool		ssl_in_use = false;
+	hbaPort	   *port = palloc0(sizeof(hbaPort));
+	TupleDesc	tupdesc;
+	Datum		values[PG_HBA_MATCHES_ATTS];
+	bool		isnull[PG_HBA_MATCHES_ATTS];
+
+	if (!is_member_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("only superuser or a member of the pg_read_server_files role may call this function")));
+
+	if (PG_ARGISNULL(0))
+		port->raddr.addr.ss_family = AF_UNIX;
+	else
+	{
+		int			bits;
+		char	   *ptr;
+		char		tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+		address = PG_GETARG_INET_PP(0);
+
+		bits = ip_maxbits(address) - ip_bits(address);
+		if (bits != 0)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("Invalid address")));
+		}
+
+		/* force display of max bits, regardless of masklen... */
+		if (pg_inet_net_ntop(ip_family(address), ip_addr(address),
+							 ip_maxbits(address), tmp, sizeof(tmp)) == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+					 errmsg("could not format inet value: %m")));
+
+		/* Suppress /n if present (shouldn't happen now) */
+		if ((ptr = strchr(tmp, '/')) != NULL)
+			*ptr = '\0';
+
+		switch (ip_family(address))
+		{
+			case PGSQL_AF_INET:
+			{
+				struct sockaddr_in *dst;
+
+				dst = (struct sockaddr_in *) &port->raddr.addr;
+				dst->sin_family = AF_INET;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin_addr, &ip_addr(address), sizeof(dst->sin_addr));
+
+				break;
+			}
+			/* See pg_inet_net_ntop() for details about those constants */
+			case PGSQL_AF_INET6:
+#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6
+			case AF_INET6:
+#endif
+			{
+				struct sockaddr_in6 *dst;
+
+				dst = (struct sockaddr_in6 *) &port->raddr.addr;
+				dst->sin6_family = AF_INET6;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin6_addr, &ip_addr(address), sizeof(dst->sin6_addr));
+
+				break;
+			}
+			default:
+				elog(ERROR, "unexpected ip_family: %d", ip_family(address));
+				break;
+		}
+	}
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("parameter role is mandatory")));
+	port->user_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
+
+	if (!PG_ARGISNULL(2))
+		ssl_in_use = PG_GETARG_BOOL(2);
+
+	port->ssl_in_use = ssl_in_use;
+
+	tupdesc = CreateTemplateTupleDesc(PG_HBA_MATCHES_ATTS);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "file_name",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "line_num",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "raw_line",
+					   TEXTOID, -1, 0);
+
+	BlessTupleDesc(tupdesc);
+
+	memset(isnull, 0, sizeof(isnull));
+
+	/* FIXME rework API to not rely on PostmasterContext */
+	ctxt = AllocSetContextCreate(CurrentMemoryContext, "load_hba",
+								 ALLOCSET_DEFAULT_SIZES);
+	PostmasterContext = AllocSetContextCreate(ctxt,
+											  "Postmaster",
+											  ALLOCSET_DEFAULT_SIZES);
+	parsed_hba_context = NULL;
+	if (!load_hba())
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("Invalidation auth configuration file")));
+
+	check_hba(port);
+
+	if (port->hba->auth_method == uaImplicitReject)
+		PG_RETURN_NULL();
+
+	values[0] = CStringGetTextDatum(port->hba->sourcefile);
+	values[1] = Int32GetDatum(port->hba->linenumber);
+	values[2] = CStringGetTextDatum(port->hba->rawline);
+
+	MemoryContextDelete(PostmasterContext);
+	PostmasterContext = NULL;
+
+	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 04cab629f5..ea7d7c7eff 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6122,6 +6122,13 @@
   proargmodes => '{o,o,o,o,o,o,o}',
   proargnames => '{mapping_number,file_name,line_number,map_name,sys_name,pg_username,error}',
   prosrc => 'pg_ident_file_mappings' },
+{ oid => '9557', descr => 'show wether the given connection would match an hba line',
+  proname => 'pg_hba_matches', provolatile => 'v', prorettype => 'record',
+  proargtypes => 'inet text bool', proisstrict => 'f',
+  proallargtypes => '{inet,text,bool,text,int4,text}',
+  proargmodes => '{i,i,i,o,o,o}',
+  proargnames => '{address,role,ssl,file_name,line_num,raw_line}',
+  prosrc => 'pg_hba_matches' },
 { oid => '1371', descr => 'view system lock information',
   proname => 'pg_lock_status', prorows => '1000', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
-- 
2.33.1


--w64sw4oo7jkfb67p--





^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* [PATCH v7 2/2] POC: Add a pg_hba_matches() function.
@ 2022-02-22 13:34 Julien Rouhaud <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Julien Rouhaud @ 2022-02-22 13:34 UTC (permalink / raw)

Catversion is bumped.

Author: Julien Rouhaud
Reviewed-by: FIXME
Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya%40jrouhaud
---
 src/backend/catalog/system_functions.sql |   9 ++
 src/backend/libpq/hba.c                  | 138 +++++++++++++++++++++++
 src/include/catalog/pg_proc.dat          |   7 ++
 3 files changed, 154 insertions(+)

diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql
index 73da687d5d..bfee72f705 100644
--- a/src/backend/catalog/system_functions.sql
+++ b/src/backend/catalog/system_functions.sql
@@ -594,6 +594,15 @@ LANGUAGE internal
 STRICT IMMUTABLE PARALLEL SAFE
 AS 'unicode_is_normalized';
 
+CREATE OR REPLACE FUNCTION
+  pg_hba_matches(
+    IN address inet, IN role text, IN ssl bool DEFAULT false,
+    OUT file_name text, OUT line_num int4, OUT raw_line text)
+RETURNS RECORD
+LANGUAGE INTERNAL
+VOLATILE
+AS 'pg_hba_matches';
+
 --
 -- The default permissions for functions mean that anyone can execute them.
 -- A number of functions shouldn't be executable by just anyone, but rather
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 56b6cec9d5..09554a9bc6 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -27,6 +27,7 @@
 #include <unistd.h>
 
 #include "access/htup_details.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "common/ip.h"
@@ -42,6 +43,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/inet.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/varlena.h"
@@ -2946,3 +2948,139 @@ hba_authname(UserAuth auth_method)
 
 	return UserAuthName[auth_method];
 }
+
+#define PG_HBA_MATCHES_ATTS	3
+
+/*
+ * SQL-accessible SRF to return the entries that match the given connection
+ * info, if any.
+ */
+Datum pg_hba_matches(PG_FUNCTION_ARGS)
+{
+	MemoryContext ctxt;
+	inet	   *address = NULL;
+	bool		ssl_in_use = false;
+	hbaPort	   *port = palloc0(sizeof(hbaPort));
+	TupleDesc	tupdesc;
+	Datum		values[PG_HBA_MATCHES_ATTS];
+	bool		isnull[PG_HBA_MATCHES_ATTS];
+
+	if (!is_member_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("only superuser or a member of the pg_read_server_files role may call this function")));
+
+	if (PG_ARGISNULL(0))
+		port->raddr.addr.ss_family = AF_UNIX;
+	else
+	{
+		int			bits;
+		char	   *ptr;
+		char		tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+		address = PG_GETARG_INET_PP(0);
+
+		bits = ip_maxbits(address) - ip_bits(address);
+		if (bits != 0)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("Invalid address")));
+		}
+
+		/* force display of max bits, regardless of masklen... */
+		if (pg_inet_net_ntop(ip_family(address), ip_addr(address),
+							 ip_maxbits(address), tmp, sizeof(tmp)) == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+					 errmsg("could not format inet value: %m")));
+
+		/* Suppress /n if present (shouldn't happen now) */
+		if ((ptr = strchr(tmp, '/')) != NULL)
+			*ptr = '\0';
+
+		switch (ip_family(address))
+		{
+			case PGSQL_AF_INET:
+			{
+				struct sockaddr_in *dst;
+
+				dst = (struct sockaddr_in *) &port->raddr.addr;
+				dst->sin_family = AF_INET;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin_addr, &ip_addr(address), sizeof(dst->sin_addr));
+
+				break;
+			}
+			/* See pg_inet_net_ntop() for details about those constants */
+			case PGSQL_AF_INET6:
+#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6
+			case AF_INET6:
+#endif
+			{
+				struct sockaddr_in6 *dst;
+
+				dst = (struct sockaddr_in6 *) &port->raddr.addr;
+				dst->sin6_family = AF_INET6;
+
+				/* ip_addr(address) always contains network representation */
+				memcpy(&dst->sin6_addr, &ip_addr(address), sizeof(dst->sin6_addr));
+
+				break;
+			}
+			default:
+				elog(ERROR, "unexpected ip_family: %d", ip_family(address));
+				break;
+		}
+	}
+
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("parameter role is mandatory")));
+	port->user_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
+
+	if (!PG_ARGISNULL(2))
+		ssl_in_use = PG_GETARG_BOOL(2);
+
+	port->ssl_in_use = ssl_in_use;
+
+	tupdesc = CreateTemplateTupleDesc(PG_HBA_MATCHES_ATTS);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "file_name",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "line_num",
+					   INT4OID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "raw_line",
+					   TEXTOID, -1, 0);
+
+	BlessTupleDesc(tupdesc);
+
+	memset(isnull, 0, sizeof(isnull));
+
+	/* FIXME rework API to not rely on PostmasterContext */
+	ctxt = AllocSetContextCreate(CurrentMemoryContext, "load_hba",
+								 ALLOCSET_DEFAULT_SIZES);
+	PostmasterContext = AllocSetContextCreate(ctxt,
+											  "Postmaster",
+											  ALLOCSET_DEFAULT_SIZES);
+	parsed_hba_context = NULL;
+	if (!load_hba())
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("Invalidation auth configuration file")));
+
+	check_hba(port);
+
+	if (port->hba->auth_method == uaImplicitReject)
+		PG_RETURN_NULL();
+
+	values[0] = CStringGetTextDatum(port->hba->sourcefile);
+	values[1] = Int32GetDatum(port->hba->linenumber);
+	values[2] = CStringGetTextDatum(port->hba->rawline);
+
+	MemoryContextDelete(PostmasterContext);
+	PostmasterContext = NULL;
+
+	return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 65bfd32753..2c65ee2019 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6139,6 +6139,13 @@
   proargmodes => '{o,o,o,o,o,o,o}',
   proargnames => '{mapping_number,file_name,line_number,map_name,sys_name,pg_username,error}',
   prosrc => 'pg_ident_file_mappings' },
+{ oid => '9557', descr => 'show wether the given connection would match an hba line',
+  proname => 'pg_hba_matches', provolatile => 'v', prorettype => 'record',
+  proargtypes => 'inet text bool', proisstrict => 'f',
+  proallargtypes => '{inet,text,bool,text,int4,text}',
+  proargmodes => '{i,i,i,o,o,o}',
+  proargnames => '{address,role,ssl,file_name,line_num,raw_line}',
+  prosrc => 'pg_hba_matches' },
 { oid => '1371', descr => 'view system lock information',
   proname => 'pg_lock_status', prorows => '1000', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
-- 
2.33.1


--o45jk7yo2ulu7ifa--





^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* Re: ensure, not insure
@ 2023-11-09 01:22 Michael Paquier <[email protected]>
  2023-11-09 11:20 ` Re: ensure, not insure David Rowley <[email protected]>
  0 siblings, 1 reply; 18+ messages in thread

From: Michael Paquier @ 2023-11-09 01:22 UTC (permalink / raw)
  To: David Rowley <[email protected]>; +Cc: Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>

On Wed, Nov 08, 2023 at 08:31:28PM +1300, David Rowley wrote:
> On Wed, 8 Nov 2023 at 19:56, Peter Smith <[email protected]> wrote:
>> In passing I found/fixed a bunch of similar misuses in comments.
> 
> Those all look fine to me too.

+1.
--
Michael


Attachments:

  [application/pgp-signature] signature.asc (833B, ../../[email protected]/2-signature.asc)
  download

^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* Re: ensure, not insure
  2023-11-09 01:22 Re: ensure, not insure Michael Paquier <[email protected]>
@ 2023-11-09 11:20 ` David Rowley <[email protected]>
  2023-11-09 15:00   ` Re: ensure, not insure Tom Lane <[email protected]>
  0 siblings, 1 reply; 18+ messages in thread

From: David Rowley @ 2023-11-09 11:20 UTC (permalink / raw)
  To: Michael Paquier <[email protected]>; +Cc: Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>

On Thu, 9 Nov 2023 at 14:22, Michael Paquier <[email protected]> wrote:
>
> On Wed, Nov 08, 2023 at 08:31:28PM +1300, David Rowley wrote:
> > Those all look fine to me too.
>
> +1.

I've pushed this.  I backpatched due to the typo in the fsync GUC
description.  I'd have only pushed to master if it were just the
comment typos.

I noticed older versions had another instance of "insure" in a code
comment. I opted to leave that one alone since that file is now gone
in more recent versions.

David






^ permalink  raw  reply  [nested|flat] 18+ messages in thread

* Re: ensure, not insure
  2023-11-09 01:22 Re: ensure, not insure Michael Paquier <[email protected]>
  2023-11-09 11:20 ` Re: ensure, not insure David Rowley <[email protected]>
@ 2023-11-09 15:00   ` Tom Lane <[email protected]>
  0 siblings, 0 replies; 18+ messages in thread

From: Tom Lane @ 2023-11-09 15:00 UTC (permalink / raw)
  To: David Rowley <[email protected]>; +Cc: Michael Paquier <[email protected]>; Peter Smith <[email protected]>; PostgreSQL Hackers <[email protected]>

David Rowley <[email protected]> writes:
> I've pushed this.  I backpatched due to the typo in the fsync GUC
> description.  I'd have only pushed to master if it were just the
> comment typos.

FTR, I do not think you should have back-patched.  You created extra
work for the translation team, and the mistake is subtle enough that
it wasn't worth that.  (My dictionary says that "insure and ensure
are often interchangeable, particularly in US English".)

			regards, tom lane






^ permalink  raw  reply  [nested|flat] 18+ messages in thread


end of thread, other threads:[~2023-11-09 15:00 UTC | newest]

Thread overview: 18+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2022-02-22 13:34 [PATCH v8 6/6] POC: Add a pg_hba_matches() function. Julien Rouhaud <[email protected]>
2022-02-22 13:34 [PATCH v7 2/2] POC: Add a pg_hba_matches() function. Julien Rouhaud <[email protected]>
2022-02-22 13:34 [PATCH v10 3/3] POC: Add a pg_hba_matches() function. Julien Rouhaud <[email protected]>
2022-02-22 13:34 [PATCH v1 3/3] POC: Add a pg_hba_matches() function. Julien Rouhaud <[email protected]>
2022-02-22 13:34 [PATCH v10 3/3] POC: Add a pg_hba_matches() function. Julien Rouhaud <[email protected]>
2022-02-22 13:34 [PATCH v9 3/3] POC: Add a pg_hba_matches() function. Julien Rouhaud <[email protected]>
2022-02-22 13:34 [PATCH v12 3/3] POC: Add a pg_hba_matches() function. Julien Rouhaud <[email protected]>
2022-02-22 13:34 [PATCH v6 2/2] POC: Add a pg_hba_matches() function. Julien Rouhaud <[email protected]>
2022-02-22 13:34 [PATCH v7 2/2] POC: Add a pg_hba_matches() function. Julien Rouhaud <[email protected]>
2022-02-22 13:34 [PATCH v11 3/3] POC: Add a pg_hba_matches() function. Julien Rouhaud <[email protected]>
2022-02-22 13:34 [PATCH v2 4/4] POC: Add a pg_hba_matches() function. Julien Rouhaud <[email protected]>
2022-02-22 13:34 [PATCH v3 4/4] POC: Add a pg_hba_matches() function. Julien Rouhaud <[email protected]>
2022-02-22 13:34 [PATCH v4 3/3] POC: Add a pg_hba_matches() function. Julien Rouhaud <[email protected]>
2022-02-22 13:34 [PATCH v5 3/3] POC: Add a pg_hba_matches() function. Julien Rouhaud <[email protected]>
2022-02-22 13:34 [PATCH v7 2/2] POC: Add a pg_hba_matches() function. Julien Rouhaud <[email protected]>
2023-11-09 01:22 Re: ensure, not insure Michael Paquier <[email protected]>
2023-11-09 11:20 ` Re: ensure, not insure David Rowley <[email protected]>
2023-11-09 15:00   ` Re: ensure, not insure Tom Lane <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox