agora inbox for pgsql-hackers@postgresql.orghelp / color / mirror / Atom feed
[PATCH v4 3/3] POC: Add a pg_hba_matches() function. 14+ messages / 2 participants [nested] [flat]
* [PATCH v4 3/3] POC: Add a pg_hba_matches() function. @ 2022-02-22 13:34 Julien Rouhaud <julien.rouhaud@free.fr> 0 siblings, 0 replies; 14+ 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] 14+ messages in thread
* [PATCH v11 3/3] POC: Add a pg_hba_matches() function. @ 2022-02-22 13:34 Julien Rouhaud <julien.rouhaud@free.fr> 0 siblings, 0 replies; 14+ 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] 14+ messages in thread
* [PATCH v12 3/3] POC: Add a pg_hba_matches() function. @ 2022-02-22 13:34 Julien Rouhaud <julien.rouhaud@free.fr> 0 siblings, 0 replies; 14+ 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] 14+ messages in thread
* [PATCH v10 3/3] POC: Add a pg_hba_matches() function. @ 2022-02-22 13:34 Julien Rouhaud <julien.rouhaud@free.fr> 0 siblings, 0 replies; 14+ 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] 14+ messages in thread
* [PATCH v9 3/3] POC: Add a pg_hba_matches() function. @ 2022-02-22 13:34 Julien Rouhaud <julien.rouhaud@free.fr> 0 siblings, 0 replies; 14+ 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] 14+ messages in thread
* [PATCH v8 6/6] POC: Add a pg_hba_matches() function. @ 2022-02-22 13:34 Julien Rouhaud <julien.rouhaud@free.fr> 0 siblings, 0 replies; 14+ 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] 14+ messages in thread
* [PATCH v7 2/2] POC: Add a pg_hba_matches() function. @ 2022-02-22 13:34 Julien Rouhaud <julien.rouhaud@free.fr> 0 siblings, 0 replies; 14+ 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] 14+ messages in thread
* [PATCH v7 2/2] POC: Add a pg_hba_matches() function. @ 2022-02-22 13:34 Julien Rouhaud <julien.rouhaud@free.fr> 0 siblings, 0 replies; 14+ 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] 14+ messages in thread
* [PATCH v3 4/4] POC: Add a pg_hba_matches() function. @ 2022-02-22 13:34 Julien Rouhaud <julien.rouhaud@free.fr> 0 siblings, 0 replies; 14+ 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] 14+ messages in thread
* [PATCH v5 3/3] POC: Add a pg_hba_matches() function. @ 2022-02-22 13:34 Julien Rouhaud <julien.rouhaud@free.fr> 0 siblings, 0 replies; 14+ 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] 14+ messages in thread
* [PATCH v6 2/2] POC: Add a pg_hba_matches() function. @ 2022-02-22 13:34 Julien Rouhaud <julien.rouhaud@free.fr> 0 siblings, 0 replies; 14+ 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] 14+ messages in thread
* [PATCH v1 3/3] POC: Add a pg_hba_matches() function. @ 2022-02-22 13:34 Julien Rouhaud <julien.rouhaud@free.fr> 0 siblings, 0 replies; 14+ 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] 14+ messages in thread
* [PATCH v2 4/4] POC: Add a pg_hba_matches() function. @ 2022-02-22 13:34 Julien Rouhaud <julien.rouhaud@free.fr> 0 siblings, 0 replies; 14+ 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] 14+ messages in thread
* [PATCH v40 1/4] Refactor index_concurrently_create_copy() for use with REPACK (CONCURRENTLY). @ 2026-03-09 10:35 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 14+ messages in thread From: Antonin Houska @ 2026-03-09 10:35 UTC (permalink / raw) This patch moves the code to index_create_copy() and adds a "concurrently" parameter so it can be used by REPACK (CONCURRENTLY). With the CONCURRENTLY option, REPACK cannot simply swap the heap file and rebuild its indexes. Instead, it needs to build a separate set of indexes (including system catalog entries) *before* the actual swap, to reduce the time AccessExclusiveLock needs to be held for. --- src/backend/catalog/index.c | 54 +++++++++++++++++++++++--------- src/backend/commands/indexcmds.c | 6 ++-- src/backend/nodes/makefuncs.c | 9 +++--- src/include/catalog/index.h | 3 ++ src/include/nodes/makefuncs.h | 4 ++- 5 files changed, 54 insertions(+), 22 deletions(-) diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 5ee6389d39c..f8e6c3d804e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1288,15 +1288,32 @@ index_create(Relation heapRelation, /* * index_concurrently_create_copy * - * Create concurrently an index based on the definition of the one provided by - * caller. The index is inserted into catalogs and needs to be built later - * on. This is called during concurrent reindex processing. - * - * "tablespaceOid" is the tablespace to use for this index. + * Variant of index_create_copy(), called during concurrent reindex + * processing. */ Oid index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, Oid tablespaceOid, const char *newName) +{ + return index_create_copy(heapRelation, oldIndexId, tablespaceOid, newName, + true); +} + +/* + * index_create_copy + * + * Create an index based on the definition of the one provided by caller. The + * index is inserted into catalogs. If 'concurrently' is TRUE, it needs to be + * built later on, otherwise it's built immediately. + * + * "tablespaceOid" is the tablespace to use for this index. + * + * The actual implementation of index_concurrently_create_copy(), reusable for + * other purposes. + */ +Oid +index_create_copy(Relation heapRelation, Oid oldIndexId, Oid tablespaceOid, + const char *newName, bool concurrently) { Relation indexRelation; IndexInfo *oldInfo, @@ -1315,6 +1332,7 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, List *indexColNames = NIL; List *indexExprs = NIL; List *indexPreds = NIL; + int flags = 0; indexRelation = index_open(oldIndexId, RowExclusiveLock); @@ -1325,7 +1343,7 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, * Concurrent build of an index with exclusion constraints is not * supported. */ - if (oldInfo->ii_ExclusionOps != NULL) + if (oldInfo->ii_ExclusionOps != NULL && concurrently) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("concurrent index creation for exclusion constraints is not supported"))); @@ -1381,9 +1399,7 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, } /* - * Build the index information for the new index. Note that rebuild of - * indexes with exclusion constraints is not supported, hence there is no - * need to fill all the ii_Exclusion* fields. + * Build the index information for the new index. */ newInfo = makeIndexInfo(oldInfo->ii_NumIndexAttrs, oldInfo->ii_NumIndexKeyAttrs, @@ -1392,10 +1408,13 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, indexPreds, oldInfo->ii_Unique, oldInfo->ii_NullsNotDistinct, - false, /* not ready for inserts */ - true, + !concurrently, /* isready */ + concurrently, /* concurrent */ indexRelation->rd_indam->amsummarizing, - oldInfo->ii_WithoutOverlaps); + oldInfo->ii_WithoutOverlaps, + oldInfo->ii_ExclusionOps, + oldInfo->ii_ExclusionProcs, + oldInfo->ii_ExclusionStrats); /* * Extract the list of column names and the column numbers for the new @@ -1433,6 +1452,9 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, stattargets[i].isnull = isnull; } + if (concurrently) + flags = INDEX_CREATE_SKIP_BUILD | INDEX_CREATE_CONCURRENT; + /* * Now create the new index. * @@ -1456,7 +1478,7 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, indcoloptions->values, stattargets, reloptionsDatum, - INDEX_CREATE_SKIP_BUILD | INDEX_CREATE_CONCURRENT, + flags, 0, true, /* allow table to be a system catalog? */ false, /* is_internal? */ @@ -2450,7 +2472,8 @@ BuildIndexInfo(Relation index) indexStruct->indisready, false, index->rd_indam->amsummarizing, - indexStruct->indisexclusion && indexStruct->indisunique); + indexStruct->indisexclusion && indexStruct->indisunique, + NULL, NULL, NULL); /* fill in attribute numbers */ for (i = 0; i < numAtts; i++) @@ -2510,7 +2533,8 @@ BuildDummyIndexInfo(Relation index) indexStruct->indisready, false, index->rd_indam->amsummarizing, - indexStruct->indisexclusion && indexStruct->indisunique); + indexStruct->indisexclusion && indexStruct->indisunique, + NULL, NULL, NULL); /* fill in attribute numbers */ for (i = 0; i < numAtts; i++) diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 635679cc1f2..34209bd1393 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -243,7 +243,8 @@ CheckIndexCompatible(Oid oldId, */ indexInfo = makeIndexInfo(numberOfAttributes, numberOfAttributes, accessMethodId, NIL, NIL, false, false, - false, false, amsummarizing, isWithoutOverlaps); + false, false, amsummarizing, isWithoutOverlaps, + NULL, NULL, NULL); typeIds = palloc_array(Oid, numberOfAttributes); collationIds = palloc_array(Oid, numberOfAttributes); opclassIds = palloc_array(Oid, numberOfAttributes); @@ -930,7 +931,8 @@ DefineIndex(ParseState *pstate, !concurrent, concurrent, amissummarizing, - stmt->iswithoutoverlaps); + stmt->iswithoutoverlaps, + NULL, NULL, NULL); typeIds = palloc_array(Oid, numberOfAttributes); collationIds = palloc_array(Oid, numberOfAttributes); diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 2caec621d73..ca7e21e8349 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -834,7 +834,8 @@ IndexInfo * makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions, List *predicates, bool unique, bool nulls_not_distinct, bool isready, bool concurrent, bool summarizing, - bool withoutoverlaps) + bool withoutoverlaps, Oid *exclusion_ops, Oid *exclusion_procs, + uint16 *exclusion_strats) { IndexInfo *n = makeNode(IndexInfo); @@ -863,9 +864,9 @@ makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions, n->ii_PredicateState = NULL; /* exclusion constraints */ - n->ii_ExclusionOps = NULL; - n->ii_ExclusionProcs = NULL; - n->ii_ExclusionStrats = NULL; + n->ii_ExclusionOps = exclusion_ops; + n->ii_ExclusionProcs = exclusion_procs; + n->ii_ExclusionStrats = exclusion_strats; /* speculative inserts */ n->ii_UniqueOps = NULL; diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index b259c4141ed..3426087b445 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -99,6 +99,9 @@ extern Oid index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, Oid tablespaceOid, const char *newName); +extern Oid index_create_copy(Relation heapRelation, Oid oldIndexId, + Oid tablespaceOid, const char *newName, + bool concurrently); extern void index_concurrently_build(Oid heapRelationId, Oid indexRelationId); diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h index 982ec25ae14..dcea148ae1a 100644 --- a/src/include/nodes/makefuncs.h +++ b/src/include/nodes/makefuncs.h @@ -99,7 +99,9 @@ extern IndexInfo *makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions, List *predicates, bool unique, bool nulls_not_distinct, bool isready, bool concurrent, - bool summarizing, bool withoutoverlaps); + bool summarizing, bool withoutoverlaps, + Oid *exclusion_ops, Oid *exclusion_procs, + uint16 *exclusion_strats); extern Node *makeStringConst(char *str, int location); extern DefElem *makeDefElem(char *name, Node *arg, int location); -- 2.47.3 --k4rn5ob3nx2ufkij Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v40-0002-Add-CONCURRENTLY-option-to-REPACK-command.patch" ^ permalink raw reply [nested|flat] 14+ messages in thread
end of thread, other threads:[~2026-03-09 10:35 UTC | newest] Thread overview: 14+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2022-02-22 13:34 [PATCH v4 3/3] POC: Add a pg_hba_matches() function. Julien Rouhaud <julien.rouhaud@free.fr> 2022-02-22 13:34 [PATCH v10 3/3] POC: Add a pg_hba_matches() function. Julien Rouhaud <julien.rouhaud@free.fr> 2022-02-22 13:34 [PATCH v8 6/6] POC: Add a pg_hba_matches() function. Julien Rouhaud <julien.rouhaud@free.fr> 2022-02-22 13:34 [PATCH v7 2/2] POC: Add a pg_hba_matches() function. Julien Rouhaud <julien.rouhaud@free.fr> 2022-02-22 13:34 [PATCH v7 2/2] POC: Add a pg_hba_matches() function. Julien Rouhaud <julien.rouhaud@free.fr> 2022-02-22 13:34 [PATCH v3 4/4] POC: Add a pg_hba_matches() function. Julien Rouhaud <julien.rouhaud@free.fr> 2022-02-22 13:34 [PATCH v6 2/2] POC: Add a pg_hba_matches() function. Julien Rouhaud <julien.rouhaud@free.fr> 2022-02-22 13:34 [PATCH v2 4/4] POC: Add a pg_hba_matches() function. Julien Rouhaud <julien.rouhaud@free.fr> 2022-02-22 13:34 [PATCH v11 3/3] POC: Add a pg_hba_matches() function. Julien Rouhaud <julien.rouhaud@free.fr> 2022-02-22 13:34 [PATCH v12 3/3] POC: Add a pg_hba_matches() function. Julien Rouhaud <julien.rouhaud@free.fr> 2022-02-22 13:34 [PATCH v9 3/3] POC: Add a pg_hba_matches() function. Julien Rouhaud <julien.rouhaud@free.fr> 2022-02-22 13:34 [PATCH v5 3/3] POC: Add a pg_hba_matches() function. Julien Rouhaud <julien.rouhaud@free.fr> 2022-02-22 13:34 [PATCH v1 3/3] POC: Add a pg_hba_matches() function. Julien Rouhaud <julien.rouhaud@free.fr> 2026-03-09 10:35 [PATCH v40 1/4] Refactor index_concurrently_create_copy() for use with REPACK (CONCURRENTLY). Antonin Houska <ah@cybertec.at>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox