From 90efa2f5565d28054c30c18f6a2f17f94fdff91e Mon Sep 17 00:00:00 2001 From: Andrew Dunstan Date: Tue, 22 Mar 2022 10:27:00 -0400 Subject: [PATCH] Add a test module for Object Access hooks This includes tests of both the newly added name type object access hooks and the older Oid type hooks, and provides a useful example of how to use the hooks. Mark Dilger, based on some code from Joshua Brindle. Discussion: https://postgr.es/m/47F87A0E-C0E5-43A6-89F6-D403F2B45175@enterprisedb.com --- src/test/modules/Makefile | 1 + src/test/modules/test_oat_hooks/.gitignore | 4 + src/test/modules/test_oat_hooks/Makefile | 24 + src/test/modules/test_oat_hooks/README | 86 ++ .../expected/test_oat_hooks.out | 209 ++++ .../test_oat_hooks/sql/test_oat_hooks.sql | 52 + .../modules/test_oat_hooks/test_oat_hooks.c | 934 ++++++++++++++++++ .../test_oat_hooks/test_oat_hooks.conf | 1 + 8 files changed, 1311 insertions(+) create mode 100644 src/test/modules/test_oat_hooks/.gitignore create mode 100644 src/test/modules/test_oat_hooks/Makefile create mode 100644 src/test/modules/test_oat_hooks/README create mode 100644 src/test/modules/test_oat_hooks/expected/test_oat_hooks.out create mode 100644 src/test/modules/test_oat_hooks/sql/test_oat_hooks.sql create mode 100644 src/test/modules/test_oat_hooks/test_oat_hooks.c create mode 100644 src/test/modules/test_oat_hooks/test_oat_hooks.conf diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile index dffc79b2d9a..9090226daa0 100644 --- a/src/test/modules/Makefile +++ b/src/test/modules/Makefile @@ -20,6 +20,7 @@ SUBDIRS = \ test_ginpostinglist \ test_integerset \ test_misc \ + test_oat_hooks \ test_parser \ test_pg_dump \ test_predtest \ diff --git a/src/test/modules/test_oat_hooks/.gitignore b/src/test/modules/test_oat_hooks/.gitignore new file mode 100644 index 00000000000..5dcb3ff9723 --- /dev/null +++ b/src/test/modules/test_oat_hooks/.gitignore @@ -0,0 +1,4 @@ +# Generated subdirectories +/log/ +/results/ +/tmp_check/ diff --git a/src/test/modules/test_oat_hooks/Makefile b/src/test/modules/test_oat_hooks/Makefile new file mode 100644 index 00000000000..d0df9c1abbf --- /dev/null +++ b/src/test/modules/test_oat_hooks/Makefile @@ -0,0 +1,24 @@ +# src/test/modules/test_oat_hooks/Makefile + +MODULE_big = test_oat_hooks +OBJS = \ + $(WIN32RES) \ + test_oat_hooks.o +PGFILEDESC = "test_oat_hooks - example use of object access hooks" + +REGRESS = test_oat_hooks +REGRESS_OPTS = --temp-config=$(top_srcdir)/src/test/modules/test_oat_hooks/test_oat_hooks.conf +# Disabled because these tests require "shared_preload_libraries=test_oat_hooks", +# which typical installcheck users do not have (e.g. buildfarm clients). +NO_INSTALLCHECK = 1 + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = src/test/modules/test_oat_hooks +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/src/test/modules/test_oat_hooks/README b/src/test/modules/test_oat_hooks/README new file mode 100644 index 00000000000..a7c400c7475 --- /dev/null +++ b/src/test/modules/test_oat_hooks/README @@ -0,0 +1,86 @@ +OVERVIEW +======== + +This test module, "test_oat_hooks", is an example of how to use the object +access hooks (OAT) to enforce mandatory access controls (MAC). + +The testing strategy is as follows: When this module loads, it registers hooks +of various types. (See below.) GUCs are defined to control each hook, +determining whether the hook allows or denies actions for which it fires. A +single additional GUC controls the verbosity of the hooks. GUCs default to +permissive/quiet, which allows the module to load without generating noise in +the log or denying any activity in the run-up to the regression test beginning. +When the test begins, it uses SET commands to turn on logging and to control +each hook's permissive/restrictive behavior. Various SQL statements are run +under both superuser and ordinary user permissions. The output is compared +against the expected output to verify that the hooks behaved and fired in the +order by expect. + +Because users may care about the firing order of other system hooks relative to +OAT hooks, ProcessUtility hooks and ExecutorCheckPerms hooks are also +registered by this module, with their own logging and allow/deny behavior. + + +SUSET test configuration GUCs +============================= + +The following configuration parameters (GUCs) control this test module's Object +Access Type (OAT), Process Utility and Executor Check Permissions hooks. The +general pattern is that each hook has a corresponding GUC which controls +whether the hook will allow or deny operations for which the hook gets called. +A real-world OAT hook should certainly provide more fine-grained conrol than +merely "allow-all" vs. "deny-all", but for testing this is sufficient. + +Note that even when these hooks allow an action, the core permissions system +may still refuse the action. The firing order of the hooks relative to the +core permissions system can be inferred from which NOTICE messages get emitted +before an action is refused. + +Each hook applies the allow vs. deny setting to all operations performed by +non-superusers. + +- test_oat_hooks.deny_set_variable + + Controls whether the object_access_hook_str MAC function rejects attempts to + set a configuration parameter. + +- test_oat_hooks.deny_alter_system + + Controls whether the object_access_hook_str MAC function rejects attempts to + alter system set a configuration parameter. + +- test_oat_hooks.deny_object_access + + Controls whether the object_access_hook MAC function rejects all operations + for which it is called. + +- test_oat_hooks.deny_exec_perms + + Controls whether the exec_check_perms MAC function rejects all operations for + which it is called. + +- test_oat_hooks.deny_utility_commands + + Controls whether the ProcessUtility_hook function rejects all operations for + which it is called. + +- test_oat_hooks.audit + + Controls whether each hook logs NOTICE messages for each attempt, along with + success or failure status. Note that clearing or setting this GUC may itself + generate NOTICE messages appearing before but not after, or after but not + before, the new setting takes effect. + + +Functions +========= + +The module registers hooks by the following names: + +- REGRESS_object_access_hook + +- REGRESS_object_access_hook_str + +- REGRESS_exec_check_perms + +- REGRESS_utility_command diff --git a/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out b/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out new file mode 100644 index 00000000000..20357695801 --- /dev/null +++ b/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out @@ -0,0 +1,209 @@ +-- SET commands fire both the ProcessUtility_hook and the +-- object_access_hook_str. Since the auditing GUC starts out false, we miss the +-- initial "attempting" audit message from the ProcessUtility_hook, but we +-- should thereafter see the audit messages +SET test_oat_hooks.audit = true; +NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.audit] +NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.audit] +NOTICE: in process utility: superuser finished set +-- Create objects for use in the test +CREATE USER regress_test_user; +NOTICE: in process utility: superuser attempting CreateRoleStmt +NOTICE: in object access: superuser attempting create (subId=0) [explicit] +NOTICE: in object access: superuser finished create (subId=0) [explicit] +NOTICE: in process utility: superuser finished CreateRoleStmt +CREATE TABLE regress_test_table (t text); +NOTICE: in process utility: superuser attempting CreateStmt +NOTICE: in object access: superuser attempting namespace search (subId=0) [no report on violation, allowed] +LINE 1: CREATE TABLE regress_test_table (t text); + ^ +NOTICE: in object access: superuser finished namespace search (subId=0) [no report on violation, allowed] +LINE 1: CREATE TABLE regress_test_table (t text); + ^ +NOTICE: in object access: superuser attempting create (subId=0) [explicit] +NOTICE: in object access: superuser finished create (subId=0) [explicit] +NOTICE: in object access: superuser attempting create (subId=0) [explicit] +NOTICE: in object access: superuser finished create (subId=0) [explicit] +NOTICE: in object access: superuser attempting create (subId=0) [explicit] +NOTICE: in object access: superuser finished create (subId=0) [explicit] +NOTICE: in object access: superuser attempting create (subId=0) [internal] +NOTICE: in object access: superuser finished create (subId=0) [internal] +NOTICE: in object access: superuser attempting create (subId=0) [internal] +NOTICE: in object access: superuser finished create (subId=0) [internal] +NOTICE: in process utility: superuser finished CreateStmt +GRANT SELECT ON Table regress_test_table TO public; +NOTICE: in process utility: superuser attempting GrantStmt +NOTICE: in process utility: superuser finished GrantStmt +CREATE FUNCTION regress_test_func (t text) RETURNS text AS $$ + SELECT $1; +$$ LANGUAGE sql; +NOTICE: in process utility: superuser attempting CreateFunctionStmt +NOTICE: in object access: superuser attempting create (subId=0) [explicit] +NOTICE: in object access: superuser finished create (subId=0) [explicit] +NOTICE: in process utility: superuser finished CreateFunctionStmt +GRANT EXECUTE ON FUNCTION regress_test_func (text) TO public; +NOTICE: in process utility: superuser attempting GrantStmt +NOTICE: in process utility: superuser finished GrantStmt +-- Do a few things as superuser +SELECT * FROM regress_test_table; +NOTICE: in executor check perms: superuser attempting execute +NOTICE: in executor check perms: superuser finished execute + t +--- +(0 rows) + +SELECT regress_test_func('arg'); +NOTICE: in executor check perms: superuser attempting execute +NOTICE: in executor check perms: superuser finished execute + regress_test_func +------------------- + arg +(1 row) + +SET work_mem = 8192; +NOTICE: in process utility: superuser attempting set +NOTICE: in object_access_hook_str: superuser attempting alter (set) [work_mem] +NOTICE: in object_access_hook_str: superuser finished alter (set) [work_mem] +NOTICE: in process utility: superuser finished set +RESET work_mem; +NOTICE: in process utility: superuser attempting set +NOTICE: in object_access_hook_str: superuser attempting alter (set) [work_mem] +NOTICE: in object_access_hook_str: superuser finished alter (set) [work_mem] +NOTICE: in process utility: superuser finished set +ALTER SYSTEM SET work_mem = 8192; +NOTICE: in process utility: superuser attempting alter system +NOTICE: in object_access_hook_str: superuser attempting alter (alter system set) [work_mem] +NOTICE: in object_access_hook_str: superuser finished alter (alter system set) [work_mem] +NOTICE: in process utility: superuser finished alter system +ALTER SYSTEM RESET work_mem; +NOTICE: in process utility: superuser attempting alter system +NOTICE: in object_access_hook_str: superuser attempting alter (alter system set) [work_mem] +NOTICE: in object_access_hook_str: superuser finished alter (alter system set) [work_mem] +NOTICE: in process utility: superuser finished alter system +-- Do those same things as non-superuser +SET SESSION AUTHORIZATION regress_test_user; +NOTICE: in process utility: superuser attempting set +NOTICE: in object_access_hook_str: non-superuser attempting alter (set) [session_authorization] +NOTICE: in object_access_hook_str: non-superuser finished alter (set) [session_authorization] +NOTICE: in process utility: non-superuser finished set +SELECT * FROM regress_test_table; +NOTICE: in object access: non-superuser attempting namespace search (subId=0) [no report on violation, allowed] +LINE 1: SELECT * FROM regress_test_table; + ^ +NOTICE: in object access: non-superuser finished namespace search (subId=0) [no report on violation, allowed] +LINE 1: SELECT * FROM regress_test_table; + ^ +NOTICE: in executor check perms: non-superuser attempting execute +NOTICE: in executor check perms: non-superuser finished execute + t +--- +(0 rows) + +SELECT regress_test_func('arg'); +NOTICE: in executor check perms: non-superuser attempting execute +NOTICE: in executor check perms: non-superuser finished execute + regress_test_func +------------------- + arg +(1 row) + +SET work_mem = 8192; +NOTICE: in process utility: non-superuser attempting set +NOTICE: in object_access_hook_str: non-superuser attempting alter (set) [work_mem] +NOTICE: in object_access_hook_str: non-superuser finished alter (set) [work_mem] +NOTICE: in process utility: non-superuser finished set +RESET work_mem; +NOTICE: in process utility: non-superuser attempting set +NOTICE: in object_access_hook_str: non-superuser attempting alter (set) [work_mem] +NOTICE: in object_access_hook_str: non-superuser finished alter (set) [work_mem] +NOTICE: in process utility: non-superuser finished set +ALTER SYSTEM SET work_mem = 8192; +NOTICE: in process utility: non-superuser attempting alter system +ERROR: must be superuser to execute ALTER SYSTEM command +ALTER SYSTEM RESET work_mem; +NOTICE: in process utility: non-superuser attempting alter system +ERROR: must be superuser to execute ALTER SYSTEM command +RESET SESSION AUTHORIZATION; +NOTICE: in process utility: non-superuser attempting set +NOTICE: in object_access_hook_str: superuser attempting alter (set) [session_authorization] +NOTICE: in object_access_hook_str: superuser finished alter (set) [session_authorization] +NOTICE: in process utility: superuser finished set +-- Turn off non-superuser permissions +SET test_oat_hooks.deny_set_variable = true; +NOTICE: in process utility: superuser attempting set +NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.deny_set_variable] +NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.deny_set_variable] +NOTICE: in process utility: superuser finished set +SET test_oat_hooks.deny_alter_system = true; +NOTICE: in process utility: superuser attempting set +NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.deny_alter_system] +NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.deny_alter_system] +NOTICE: in process utility: superuser finished set +SET test_oat_hooks.deny_object_access = true; +NOTICE: in process utility: superuser attempting set +NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.deny_object_access] +NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.deny_object_access] +NOTICE: in process utility: superuser finished set +SET test_oat_hooks.deny_exec_perms = true; +NOTICE: in process utility: superuser attempting set +NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.deny_exec_perms] +NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.deny_exec_perms] +NOTICE: in process utility: superuser finished set +SET test_oat_hooks.deny_utility_commands = true; +NOTICE: in process utility: superuser attempting set +NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.deny_utility_commands] +NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.deny_utility_commands] +NOTICE: in process utility: superuser finished set +-- Try again as non-superuser with permisisons denied +SET SESSION AUTHORIZATION regress_test_user; +NOTICE: in process utility: superuser attempting set +NOTICE: in object_access_hook_str: non-superuser attempting alter (set) [session_authorization] +ERROR: permission denied: set session_authorization +SELECT * FROM regress_test_table; +NOTICE: in object access: superuser attempting namespace search (subId=0) [no report on violation, allowed] +LINE 1: SELECT * FROM regress_test_table; + ^ +NOTICE: in object access: superuser finished namespace search (subId=0) [no report on violation, allowed] +LINE 1: SELECT * FROM regress_test_table; + ^ +NOTICE: in executor check perms: superuser attempting execute +NOTICE: in executor check perms: superuser finished execute + t +--- +(0 rows) + +SELECT regress_test_func('arg'); +NOTICE: in executor check perms: superuser attempting execute +NOTICE: in executor check perms: superuser finished execute + regress_test_func +------------------- + arg +(1 row) + +SET work_mem = 8192; +NOTICE: in process utility: superuser attempting set +NOTICE: in object_access_hook_str: superuser attempting alter (set) [work_mem] +NOTICE: in object_access_hook_str: superuser finished alter (set) [work_mem] +NOTICE: in process utility: superuser finished set +RESET work_mem; +NOTICE: in process utility: superuser attempting set +NOTICE: in object_access_hook_str: superuser attempting alter (set) [work_mem] +NOTICE: in object_access_hook_str: superuser finished alter (set) [work_mem] +NOTICE: in process utility: superuser finished set +ALTER SYSTEM SET work_mem = 8192; +NOTICE: in process utility: superuser attempting alter system +NOTICE: in object_access_hook_str: superuser attempting alter (alter system set) [work_mem] +NOTICE: in object_access_hook_str: superuser finished alter (alter system set) [work_mem] +NOTICE: in process utility: superuser finished alter system +ALTER SYSTEM RESET work_mem; +NOTICE: in process utility: superuser attempting alter system +NOTICE: in object_access_hook_str: superuser attempting alter (alter system set) [work_mem] +NOTICE: in object_access_hook_str: superuser finished alter (alter system set) [work_mem] +NOTICE: in process utility: superuser finished alter system +RESET SESSION AUTHORIZATION; +NOTICE: in process utility: superuser attempting set +NOTICE: in object_access_hook_str: superuser attempting alter (set) [session_authorization] +NOTICE: in object_access_hook_str: superuser finished alter (set) [session_authorization] +NOTICE: in process utility: superuser finished set +SET test_oat_hooks.audit = false; +NOTICE: in process utility: superuser attempting set diff --git a/src/test/modules/test_oat_hooks/sql/test_oat_hooks.sql b/src/test/modules/test_oat_hooks/sql/test_oat_hooks.sql new file mode 100644 index 00000000000..5fb3a2f0ea4 --- /dev/null +++ b/src/test/modules/test_oat_hooks/sql/test_oat_hooks.sql @@ -0,0 +1,52 @@ +-- SET commands fire both the ProcessUtility_hook and the +-- object_access_hook_str. Since the auditing GUC starts out false, we miss the +-- initial "attempting" audit message from the ProcessUtility_hook, but we +-- should thereafter see the audit messages +SET test_oat_hooks.audit = true; + +-- Create objects for use in the test +CREATE USER regress_test_user; +CREATE TABLE regress_test_table (t text); +GRANT SELECT ON Table regress_test_table TO public; +CREATE FUNCTION regress_test_func (t text) RETURNS text AS $$ + SELECT $1; +$$ LANGUAGE sql; +GRANT EXECUTE ON FUNCTION regress_test_func (text) TO public; + +-- Do a few things as superuser +SELECT * FROM regress_test_table; +SELECT regress_test_func('arg'); +SET work_mem = 8192; +RESET work_mem; +ALTER SYSTEM SET work_mem = 8192; +ALTER SYSTEM RESET work_mem; + +-- Do those same things as non-superuser +SET SESSION AUTHORIZATION regress_test_user; +SELECT * FROM regress_test_table; +SELECT regress_test_func('arg'); +SET work_mem = 8192; +RESET work_mem; +ALTER SYSTEM SET work_mem = 8192; +ALTER SYSTEM RESET work_mem; +RESET SESSION AUTHORIZATION; + +-- Turn off non-superuser permissions +SET test_oat_hooks.deny_set_variable = true; +SET test_oat_hooks.deny_alter_system = true; +SET test_oat_hooks.deny_object_access = true; +SET test_oat_hooks.deny_exec_perms = true; +SET test_oat_hooks.deny_utility_commands = true; + +-- Try again as non-superuser with permisisons denied +SET SESSION AUTHORIZATION regress_test_user; +SELECT * FROM regress_test_table; +SELECT regress_test_func('arg'); +SET work_mem = 8192; +RESET work_mem; +ALTER SYSTEM SET work_mem = 8192; +ALTER SYSTEM RESET work_mem; + +RESET SESSION AUTHORIZATION; + +SET test_oat_hooks.audit = false; diff --git a/src/test/modules/test_oat_hooks/test_oat_hooks.c b/src/test/modules/test_oat_hooks/test_oat_hooks.c new file mode 100644 index 00000000000..b1709f4d63d --- /dev/null +++ b/src/test/modules/test_oat_hooks/test_oat_hooks.c @@ -0,0 +1,934 @@ +/*-------------------------------------------------------------------------- + * + * test_oat_hooks.c + * Code for testing mandatory access control (MAC) using object access hooks. + * + * Copyright (c) 2015-2022, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/test/modules/test_oat_hooks/test_oat_hooks.c + * + * ------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "catalog/dependency.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_proc.h" +#include "executor/executor.h" +#include "fmgr.h" +#include "miscadmin.h" +#include "tcop/utility.h" + +PG_MODULE_MAGIC; + +/* + * GUCs controlling which operations to deny + */ +static bool REGRESS_deny_set_variable = false; +static bool REGRESS_deny_alter_system = false; +static bool REGRESS_deny_object_access = false; +static bool REGRESS_deny_exec_perms = false; +static bool REGRESS_deny_utility_commands = false; +static bool REGRESS_audit = false; + +/* Saved hook values in case of unload */ +static object_access_hook_type next_object_access_hook = NULL; +static object_access_hook_type_str next_object_access_hook_str = NULL; +static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL; +static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; + +/* Test Object Access Type Hook hooks */ +static void REGRESS_object_access_hook_str(ObjectAccessType access, + Oid classId, const char *objName, + int subId, void *arg); +static void REGRESS_object_access_hook(ObjectAccessType access, Oid classId, + Oid objectId, int subId, void *arg); +static bool REGRESS_exec_check_perms(List *rangeTabls, bool do_abort); +static void REGRESS_utility_command(PlannedStmt *pstmt, + const char *queryString, bool readOnlyTree, + ProcessUtilityContext context, + ParamListInfo params, + QueryEnvironment *queryEnv, + DestReceiver *dest, QueryCompletion *qc); + +/* Helper functions */ +static const char *nodetag_to_string(NodeTag tag); +static char *accesstype_to_string(ObjectAccessType access, int subId); +static char *accesstype_arg_to_string(ObjectAccessType access, void *arg); + + +void _PG_init(void); +void _PG_fini(void); + +/* + * Module load/unload callback + */ +void +_PG_init(void) +{ + /* + * We allow to load the Object Access Type test module on single-user-mode + * or shared_preload_libraries settings only. + */ + if (IsUnderPostmaster) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("test_oat_hooks must be loaded via shared_preload_libraries"))); + + /* + * test_oat_hooks.deny_set_variable = (on|off) + */ + DefineCustomBoolVariable("test_oat_hooks.deny_set_variable", + "Deny non-superuser set permissions", + NULL, + ®RESS_deny_set_variable, + false, + PGC_SUSET, + GUC_NOT_IN_SAMPLE, + NULL, + NULL, + NULL); + + /* + * test_oat_hooks.deny_alter_system = (on|off) + */ + DefineCustomBoolVariable("test_oat_hooks.deny_alter_system", + "Deny non-superuser alter system set permissions", + NULL, + ®RESS_deny_alter_system, + false, + PGC_SUSET, + GUC_NOT_IN_SAMPLE, + NULL, + NULL, + NULL); + + /* + * test_oat_hooks.deny_object_access = (on|off) + */ + DefineCustomBoolVariable("test_oat_hooks.deny_object_access", + "Deny non-superuser object access permissions", + NULL, + ®RESS_deny_object_access, + false, + PGC_SUSET, + GUC_NOT_IN_SAMPLE, + NULL, + NULL, + NULL); + + /* + * test_oat_hooks.deny_exec_perms = (on|off) + */ + DefineCustomBoolVariable("test_oat_hooks.deny_exec_perms", + "Deny non-superuser exec permissions", + NULL, + ®RESS_deny_exec_perms, + false, + PGC_SUSET, + GUC_NOT_IN_SAMPLE, + NULL, + NULL, + NULL); + + /* + * test_oat_hooks.deny_utility_commands = (on|off) + */ + DefineCustomBoolVariable("test_oat_hooks.deny_utility_commands", + "Deny non-superuser utility commands", + NULL, + ®RESS_deny_utility_commands, + false, + PGC_SUSET, + GUC_NOT_IN_SAMPLE, + NULL, + NULL, + NULL); + + /* + * test_oat_hooks.audit = (on|off) + */ + DefineCustomBoolVariable("test_oat_hooks.audit", + "Turn on/off debug audit messages", + NULL, + ®RESS_audit, + false, + PGC_SUSET, + GUC_NOT_IN_SAMPLE, + NULL, + NULL, + NULL); + + MarkGUCPrefixReserved("test_oat_hooks"); + + /* Object access hook */ + next_object_access_hook = object_access_hook; + object_access_hook = REGRESS_object_access_hook; + + /* Object access hook str */ + next_object_access_hook_str = object_access_hook_str; + object_access_hook_str = REGRESS_object_access_hook_str; + + /* DML permission check */ + next_exec_check_perms_hook = ExecutorCheckPerms_hook; + ExecutorCheckPerms_hook = REGRESS_exec_check_perms; + + /* ProcessUtility hook */ + next_ProcessUtility_hook = ProcessUtility_hook; + ProcessUtility_hook = REGRESS_utility_command; +} + +void +_PG_fini(void) +{ + /* Unload hooks */ + if (object_access_hook == REGRESS_object_access_hook) + object_access_hook = next_object_access_hook; + + if (object_access_hook_str == REGRESS_object_access_hook_str) + object_access_hook_str = next_object_access_hook_str; + + if (ExecutorCheckPerms_hook == REGRESS_exec_check_perms) + ExecutorCheckPerms_hook = next_exec_check_perms_hook; + + if (ProcessUtility_hook == REGRESS_utility_command) + ProcessUtility_hook = next_ProcessUtility_hook; +} + +static void +emit_audit_message(const char *type, const char *hook, char *action, char *objName) +{ + if (REGRESS_audit) + { + const char *who = superuser_arg(GetUserId()) ? "superuser" : "non-superuser"; + + if (objName) + ereport(NOTICE, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("in %s: %s %s %s [%s]", hook, who, type, action, objName))); + else + ereport(NOTICE, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("in %s: %s %s %s", hook, who, type, action))); + } + + if (action) + pfree(action); + if (objName) + pfree(objName); +} + +static void +audit_attempt(const char *hook, char *action, char *objName) +{ + emit_audit_message("attempting", hook, action, objName); +} + +static void +audit_success(const char *hook, char *action, char *objName) +{ + emit_audit_message("finished", hook, action, objName); +} + +static void +audit_failure(const char *hook, char *action, char *objName) +{ + emit_audit_message("denied", hook, action, objName); +} + +static void +REGRESS_object_access_hook_str(ObjectAccessType access, Oid classId, const char *objName, int subId, void *arg) +{ + audit_attempt("object_access_hook_str", + accesstype_to_string(access, subId), + pstrdup(objName)); + + if (next_object_access_hook_str) + { + (*next_object_access_hook_str)(access, classId, objName, subId, arg); + } + + switch (access) + { + case OAT_POST_ALTER: + if (subId & ACL_SET_VALUE) + { + if (REGRESS_deny_set_variable && !superuser_arg(GetUserId())) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied: set %s", objName))); + } + else if (subId & ACL_ALTER_SYSTEM) + { + if (REGRESS_deny_alter_system && !superuser_arg(GetUserId())) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied: alter system set %s", objName))); + } + else + elog(ERROR, "Unknown SettingAclRelationId subId: %d", subId); + break; + default: + break; + } + + audit_success("object_access_hook_str", + accesstype_to_string(access, subId), + pstrdup(objName)); +} + +static void +REGRESS_object_access_hook (ObjectAccessType access, Oid classId, Oid objectId, int subId, void *arg) +{ + audit_attempt("object access", + accesstype_to_string(access, 0), + accesstype_arg_to_string(access, arg)); + + if (REGRESS_deny_object_access && !superuser_arg(GetUserId())) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied: %s [%s]", + accesstype_to_string(access, 0), + accesstype_arg_to_string(access, arg)))); + + /* Forward to next hook in the chain */ + if (next_object_access_hook) + (*next_object_access_hook)(access, classId, objectId, subId, arg); + + audit_success("object access", + accesstype_to_string(access, 0), + accesstype_arg_to_string(access, arg)); +} + +static bool +REGRESS_exec_check_perms(List *rangeTabls, bool do_abort) +{ + bool am_super = superuser_arg(GetUserId()); + bool allow = true; + + audit_attempt("executor check perms", pstrdup("execute"), NULL); + + /* Perform our check */ + allow = !REGRESS_deny_exec_perms || am_super; + if (do_abort && !allow) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied: %s", "execute"))); + + /* Forward to next hook in the chain */ + if (next_exec_check_perms_hook && + !(*next_exec_check_perms_hook) (rangeTabls, do_abort)) + allow = false; + + if (allow) + audit_success("executor check perms", + pstrdup("execute"), + NULL); + else + audit_failure("executor check perms", + pstrdup("execute"), + NULL); + + return allow; +} + +static void +REGRESS_utility_command(PlannedStmt *pstmt, + const char *queryString, + bool readOnlyTree, + ProcessUtilityContext context, + ParamListInfo params, + QueryEnvironment *queryEnv, + DestReceiver *dest, + QueryCompletion *qc) +{ + Node *parsetree = pstmt->utilityStmt; + + const char *action; + NodeTag tag = nodeTag(parsetree); + + switch (tag) + { + case T_VariableSetStmt: + action = "set"; + break; + case T_AlterSystemStmt: + action = "alter system"; + break; + case T_LoadStmt: + action = "load"; + break; + default: + action = nodetag_to_string(tag); + break; + } + + audit_attempt("process utility", + pstrdup(action), + NULL); + + /* Check permissions */ + if (REGRESS_deny_utility_commands && !superuser_arg(GetUserId())) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied: %s", action))); + + /* Forward to next hook in the chain */ + if (next_ProcessUtility_hook) + (*next_ProcessUtility_hook) (pstmt, queryString, readOnlyTree, + context, params, queryEnv, + dest, qc); + else + standard_ProcessUtility(pstmt, queryString, readOnlyTree, + context, params, queryEnv, + dest, qc); + + /* We're done */ + audit_success("process utility", + pstrdup(action), + NULL); +} + +static const char * +nodetag_to_string(NodeTag tag) +{ + switch (tag) + { + case T_Invalid: return "Invalid"; break; + case T_IndexInfo: return "IndexInfo"; break; + case T_ExprContext: return "ExprContext"; break; + case T_ProjectionInfo: return "ProjectionInfo"; break; + case T_JunkFilter: return "JunkFilter"; break; + case T_OnConflictSetState: return "OnConflictSetState"; break; + case T_ResultRelInfo: return "ResultRelInfo"; break; + case T_EState: return "EState"; break; + case T_TupleTableSlot: return "TupleTableSlot"; break; + case T_Plan: return "Plan"; break; + case T_Result: return "Result"; break; + case T_ProjectSet: return "ProjectSet"; break; + case T_ModifyTable: return "ModifyTable"; break; + case T_Append: return "Append"; break; + case T_MergeAppend: return "MergeAppend"; break; + case T_RecursiveUnion: return "RecursiveUnion"; break; + case T_BitmapAnd: return "BitmapAnd"; break; + case T_BitmapOr: return "BitmapOr"; break; + case T_Scan: return "Scan"; break; + case T_SeqScan: return "SeqScan"; break; + case T_SampleScan: return "SampleScan"; break; + case T_IndexScan: return "IndexScan"; break; + case T_IndexOnlyScan: return "IndexOnlyScan"; break; + case T_BitmapIndexScan: return "BitmapIndexScan"; break; + case T_BitmapHeapScan: return "BitmapHeapScan"; break; + case T_TidScan: return "TidScan"; break; + case T_TidRangeScan: return "TidRangeScan"; break; + case T_SubqueryScan: return "SubqueryScan"; break; + case T_FunctionScan: return "FunctionScan"; break; + case T_ValuesScan: return "ValuesScan"; break; + case T_TableFuncScan: return "TableFuncScan"; break; + case T_CteScan: return "CteScan"; break; + case T_NamedTuplestoreScan: return "NamedTuplestoreScan"; break; + case T_WorkTableScan: return "WorkTableScan"; break; + case T_ForeignScan: return "ForeignScan"; break; + case T_CustomScan: return "CustomScan"; break; + case T_Join: return "Join"; break; + case T_NestLoop: return "NestLoop"; break; + case T_MergeJoin: return "MergeJoin"; break; + case T_HashJoin: return "HashJoin"; break; + case T_Material: return "Material"; break; + case T_Memoize: return "Memoize"; break; + case T_Sort: return "Sort"; break; + case T_IncrementalSort: return "IncrementalSort"; break; + case T_Group: return "Group"; break; + case T_Agg: return "Agg"; break; + case T_WindowAgg: return "WindowAgg"; break; + case T_Unique: return "Unique"; break; + case T_Gather: return "Gather"; break; + case T_GatherMerge: return "GatherMerge"; break; + case T_Hash: return "Hash"; break; + case T_SetOp: return "SetOp"; break; + case T_LockRows: return "LockRows"; break; + case T_Limit: return "Limit"; break; + case T_NestLoopParam: return "NestLoopParam"; break; + case T_PlanRowMark: return "PlanRowMark"; break; + case T_PartitionPruneInfo: return "PartitionPruneInfo"; break; + case T_PartitionedRelPruneInfo: return "PartitionedRelPruneInfo"; break; + case T_PartitionPruneStepOp: return "PartitionPruneStepOp"; break; + case T_PartitionPruneStepCombine: return "PartitionPruneStepCombine"; break; + case T_PlanInvalItem: return "PlanInvalItem"; break; + case T_PlanState: return "PlanState"; break; + case T_ResultState: return "ResultState"; break; + case T_ProjectSetState: return "ProjectSetState"; break; + case T_ModifyTableState: return "ModifyTableState"; break; + case T_AppendState: return "AppendState"; break; + case T_MergeAppendState: return "MergeAppendState"; break; + case T_RecursiveUnionState: return "RecursiveUnionState"; break; + case T_BitmapAndState: return "BitmapAndState"; break; + case T_BitmapOrState: return "BitmapOrState"; break; + case T_ScanState: return "ScanState"; break; + case T_SeqScanState: return "SeqScanState"; break; + case T_SampleScanState: return "SampleScanState"; break; + case T_IndexScanState: return "IndexScanState"; break; + case T_IndexOnlyScanState: return "IndexOnlyScanState"; break; + case T_BitmapIndexScanState: return "BitmapIndexScanState"; break; + case T_BitmapHeapScanState: return "BitmapHeapScanState"; break; + case T_TidScanState: return "TidScanState"; break; + case T_TidRangeScanState: return "TidRangeScanState"; break; + case T_SubqueryScanState: return "SubqueryScanState"; break; + case T_FunctionScanState: return "FunctionScanState"; break; + case T_TableFuncScanState: return "TableFuncScanState"; break; + case T_ValuesScanState: return "ValuesScanState"; break; + case T_CteScanState: return "CteScanState"; break; + case T_NamedTuplestoreScanState: return "NamedTuplestoreScanState"; break; + case T_WorkTableScanState: return "WorkTableScanState"; break; + case T_ForeignScanState: return "ForeignScanState"; break; + case T_CustomScanState: return "CustomScanState"; break; + case T_JoinState: return "JoinState"; break; + case T_NestLoopState: return "NestLoopState"; break; + case T_MergeJoinState: return "MergeJoinState"; break; + case T_HashJoinState: return "HashJoinState"; break; + case T_MaterialState: return "MaterialState"; break; + case T_MemoizeState: return "MemoizeState"; break; + case T_SortState: return "SortState"; break; + case T_IncrementalSortState: return "IncrementalSortState"; break; + case T_GroupState: return "GroupState"; break; + case T_AggState: return "AggState"; break; + case T_WindowAggState: return "WindowAggState"; break; + case T_UniqueState: return "UniqueState"; break; + case T_GatherState: return "GatherState"; break; + case T_GatherMergeState: return "GatherMergeState"; break; + case T_HashState: return "HashState"; break; + case T_SetOpState: return "SetOpState"; break; + case T_LockRowsState: return "LockRowsState"; break; + case T_LimitState: return "LimitState"; break; + case T_Alias: return "Alias"; break; + case T_RangeVar: return "RangeVar"; break; + case T_TableFunc: return "TableFunc"; break; + case T_Var: return "Var"; break; + case T_Const: return "Const"; break; + case T_Param: return "Param"; break; + case T_Aggref: return "Aggref"; break; + case T_GroupingFunc: return "GroupingFunc"; break; + case T_WindowFunc: return "WindowFunc"; break; + case T_SubscriptingRef: return "SubscriptingRef"; break; + case T_FuncExpr: return "FuncExpr"; break; + case T_NamedArgExpr: return "NamedArgExpr"; break; + case T_OpExpr: return "OpExpr"; break; + case T_DistinctExpr: return "DistinctExpr"; break; + case T_NullIfExpr: return "NullIfExpr"; break; + case T_ScalarArrayOpExpr: return "ScalarArrayOpExpr"; break; + case T_BoolExpr: return "BoolExpr"; break; + case T_SubLink: return "SubLink"; break; + case T_SubPlan: return "SubPlan"; break; + case T_AlternativeSubPlan: return "AlternativeSubPlan"; break; + case T_FieldSelect: return "FieldSelect"; break; + case T_FieldStore: return "FieldStore"; break; + case T_RelabelType: return "RelabelType"; break; + case T_CoerceViaIO: return "CoerceViaIO"; break; + case T_ArrayCoerceExpr: return "ArrayCoerceExpr"; break; + case T_ConvertRowtypeExpr: return "ConvertRowtypeExpr"; break; + case T_CollateExpr: return "CollateExpr"; break; + case T_CaseExpr: return "CaseExpr"; break; + case T_CaseWhen: return "CaseWhen"; break; + case T_CaseTestExpr: return "CaseTestExpr"; break; + case T_ArrayExpr: return "ArrayExpr"; break; + case T_RowExpr: return "RowExpr"; break; + case T_RowCompareExpr: return "RowCompareExpr"; break; + case T_CoalesceExpr: return "CoalesceExpr"; break; + case T_MinMaxExpr: return "MinMaxExpr"; break; + case T_SQLValueFunction: return "SQLValueFunction"; break; + case T_XmlExpr: return "XmlExpr"; break; + case T_NullTest: return "NullTest"; break; + case T_BooleanTest: return "BooleanTest"; break; + case T_CoerceToDomain: return "CoerceToDomain"; break; + case T_CoerceToDomainValue: return "CoerceToDomainValue"; break; + case T_SetToDefault: return "SetToDefault"; break; + case T_CurrentOfExpr: return "CurrentOfExpr"; break; + case T_NextValueExpr: return "NextValueExpr"; break; + case T_InferenceElem: return "InferenceElem"; break; + case T_TargetEntry: return "TargetEntry"; break; + case T_RangeTblRef: return "RangeTblRef"; break; + case T_JoinExpr: return "JoinExpr"; break; + case T_FromExpr: return "FromExpr"; break; + case T_OnConflictExpr: return "OnConflictExpr"; break; + case T_IntoClause: return "IntoClause"; break; + case T_ExprState: return "ExprState"; break; + case T_WindowFuncExprState: return "WindowFuncExprState"; break; + case T_SetExprState: return "SetExprState"; break; + case T_SubPlanState: return "SubPlanState"; break; + case T_DomainConstraintState: return "DomainConstraintState"; break; + case T_PlannerInfo: return "PlannerInfo"; break; + case T_PlannerGlobal: return "PlannerGlobal"; break; + case T_RelOptInfo: return "RelOptInfo"; break; + case T_IndexOptInfo: return "IndexOptInfo"; break; + case T_ForeignKeyOptInfo: return "ForeignKeyOptInfo"; break; + case T_ParamPathInfo: return "ParamPathInfo"; break; + case T_Path: return "Path"; break; + case T_IndexPath: return "IndexPath"; break; + case T_BitmapHeapPath: return "BitmapHeapPath"; break; + case T_BitmapAndPath: return "BitmapAndPath"; break; + case T_BitmapOrPath: return "BitmapOrPath"; break; + case T_TidPath: return "TidPath"; break; + case T_TidRangePath: return "TidRangePath"; break; + case T_SubqueryScanPath: return "SubqueryScanPath"; break; + case T_ForeignPath: return "ForeignPath"; break; + case T_CustomPath: return "CustomPath"; break; + case T_NestPath: return "NestPath"; break; + case T_MergePath: return "MergePath"; break; + case T_HashPath: return "HashPath"; break; + case T_AppendPath: return "AppendPath"; break; + case T_MergeAppendPath: return "MergeAppendPath"; break; + case T_GroupResultPath: return "GroupResultPath"; break; + case T_MaterialPath: return "MaterialPath"; break; + case T_MemoizePath: return "MemoizePath"; break; + case T_UniquePath: return "UniquePath"; break; + case T_GatherPath: return "GatherPath"; break; + case T_GatherMergePath: return "GatherMergePath"; break; + case T_ProjectionPath: return "ProjectionPath"; break; + case T_ProjectSetPath: return "ProjectSetPath"; break; + case T_SortPath: return "SortPath"; break; + case T_IncrementalSortPath: return "IncrementalSortPath"; break; + case T_GroupPath: return "GroupPath"; break; + case T_UpperUniquePath: return "UpperUniquePath"; break; + case T_AggPath: return "AggPath"; break; + case T_GroupingSetsPath: return "GroupingSetsPath"; break; + case T_MinMaxAggPath: return "MinMaxAggPath"; break; + case T_WindowAggPath: return "WindowAggPath"; break; + case T_SetOpPath: return "SetOpPath"; break; + case T_RecursiveUnionPath: return "RecursiveUnionPath"; break; + case T_LockRowsPath: return "LockRowsPath"; break; + case T_ModifyTablePath: return "ModifyTablePath"; break; + case T_LimitPath: return "LimitPath"; break; + case T_EquivalenceClass: return "EquivalenceClass"; break; + case T_EquivalenceMember: return "EquivalenceMember"; break; + case T_PathKey: return "PathKey"; break; + case T_PathTarget: return "PathTarget"; break; + case T_RestrictInfo: return "RestrictInfo"; break; + case T_IndexClause: return "IndexClause"; break; + case T_PlaceHolderVar: return "PlaceHolderVar"; break; + case T_SpecialJoinInfo: return "SpecialJoinInfo"; break; + case T_AppendRelInfo: return "AppendRelInfo"; break; + case T_RowIdentityVarInfo: return "RowIdentityVarInfo"; break; + case T_PlaceHolderInfo: return "PlaceHolderInfo"; break; + case T_MinMaxAggInfo: return "MinMaxAggInfo"; break; + case T_PlannerParamItem: return "PlannerParamItem"; break; + case T_RollupData: return "RollupData"; break; + case T_GroupingSetData: return "GroupingSetData"; break; + case T_StatisticExtInfo: return "StatisticExtInfo"; break; + case T_AllocSetContext: return "AllocSetContext"; break; + case T_SlabContext: return "SlabContext"; break; + case T_GenerationContext: return "GenerationContext"; break; + case T_Integer: return "Integer"; break; + case T_Float: return "Float"; break; + case T_Boolean: return "Boolean"; break; + case T_String: return "String"; break; + case T_BitString: return "BitString"; break; + case T_List: return "List"; break; + case T_IntList: return "IntList"; break; + case T_OidList: return "OidList"; break; + case T_ExtensibleNode: return "ExtensibleNode"; break; + case T_RawStmt: return "RawStmt"; break; + case T_Query: return "Query"; break; + case T_PlannedStmt: return "PlannedStmt"; break; + case T_InsertStmt: return "InsertStmt"; break; + case T_DeleteStmt: return "DeleteStmt"; break; + case T_UpdateStmt: return "UpdateStmt"; break; + case T_SelectStmt: return "SelectStmt"; break; + case T_ReturnStmt: return "ReturnStmt"; break; + case T_PLAssignStmt: return "PLAssignStmt"; break; + case T_AlterTableStmt: return "AlterTableStmt"; break; + case T_AlterTableCmd: return "AlterTableCmd"; break; + case T_AlterDomainStmt: return "AlterDomainStmt"; break; + case T_SetOperationStmt: return "SetOperationStmt"; break; + case T_GrantStmt: return "GrantStmt"; break; + case T_GrantRoleStmt: return "GrantRoleStmt"; break; + case T_AlterDefaultPrivilegesStmt: return "AlterDefaultPrivilegesStmt"; break; + case T_ClosePortalStmt: return "ClosePortalStmt"; break; + case T_ClusterStmt: return "ClusterStmt"; break; + case T_CopyStmt: return "CopyStmt"; break; + case T_CreateStmt: return "CreateStmt"; break; + case T_DefineStmt: return "DefineStmt"; break; + case T_DropStmt: return "DropStmt"; break; + case T_TruncateStmt: return "TruncateStmt"; break; + case T_CommentStmt: return "CommentStmt"; break; + case T_FetchStmt: return "FetchStmt"; break; + case T_IndexStmt: return "IndexStmt"; break; + case T_CreateFunctionStmt: return "CreateFunctionStmt"; break; + case T_AlterFunctionStmt: return "AlterFunctionStmt"; break; + case T_DoStmt: return "DoStmt"; break; + case T_RenameStmt: return "RenameStmt"; break; + case T_RuleStmt: return "RuleStmt"; break; + case T_NotifyStmt: return "NotifyStmt"; break; + case T_ListenStmt: return "ListenStmt"; break; + case T_UnlistenStmt: return "UnlistenStmt"; break; + case T_TransactionStmt: return "TransactionStmt"; break; + case T_ViewStmt: return "ViewStmt"; break; + case T_LoadStmt: return "LoadStmt"; break; + case T_CreateDomainStmt: return "CreateDomainStmt"; break; + case T_CreatedbStmt: return "CreatedbStmt"; break; + case T_DropdbStmt: return "DropdbStmt"; break; + case T_VacuumStmt: return "VacuumStmt"; break; + case T_ExplainStmt: return "ExplainStmt"; break; + case T_CreateTableAsStmt: return "CreateTableAsStmt"; break; + case T_CreateSeqStmt: return "CreateSeqStmt"; break; + case T_AlterSeqStmt: return "AlterSeqStmt"; break; + case T_VariableSetStmt: return "VariableSetStmt"; break; + case T_VariableShowStmt: return "VariableShowStmt"; break; + case T_DiscardStmt: return "DiscardStmt"; break; + case T_CreateTrigStmt: return "CreateTrigStmt"; break; + case T_CreatePLangStmt: return "CreatePLangStmt"; break; + case T_CreateRoleStmt: return "CreateRoleStmt"; break; + case T_AlterRoleStmt: return "AlterRoleStmt"; break; + case T_DropRoleStmt: return "DropRoleStmt"; break; + case T_LockStmt: return "LockStmt"; break; + case T_ConstraintsSetStmt: return "ConstraintsSetStmt"; break; + case T_ReindexStmt: return "ReindexStmt"; break; + case T_CheckPointStmt: return "CheckPointStmt"; break; + case T_CreateSchemaStmt: return "CreateSchemaStmt"; break; + case T_AlterDatabaseStmt: return "AlterDatabaseStmt"; break; + case T_AlterDatabaseRefreshCollStmt: return "AlterDatabaseRefreshCollStmt"; break; + case T_AlterDatabaseSetStmt: return "AlterDatabaseSetStmt"; break; + case T_AlterRoleSetStmt: return "AlterRoleSetStmt"; break; + case T_CreateConversionStmt: return "CreateConversionStmt"; break; + case T_CreateCastStmt: return "CreateCastStmt"; break; + case T_CreateOpClassStmt: return "CreateOpClassStmt"; break; + case T_CreateOpFamilyStmt: return "CreateOpFamilyStmt"; break; + case T_AlterOpFamilyStmt: return "AlterOpFamilyStmt"; break; + case T_PrepareStmt: return "PrepareStmt"; break; + case T_ExecuteStmt: return "ExecuteStmt"; break; + case T_DeallocateStmt: return "DeallocateStmt"; break; + case T_DeclareCursorStmt: return "DeclareCursorStmt"; break; + case T_CreateTableSpaceStmt: return "CreateTableSpaceStmt"; break; + case T_DropTableSpaceStmt: return "DropTableSpaceStmt"; break; + case T_AlterObjectDependsStmt: return "AlterObjectDependsStmt"; break; + case T_AlterObjectSchemaStmt: return "AlterObjectSchemaStmt"; break; + case T_AlterOwnerStmt: return "AlterOwnerStmt"; break; + case T_AlterOperatorStmt: return "AlterOperatorStmt"; break; + case T_AlterTypeStmt: return "AlterTypeStmt"; break; + case T_DropOwnedStmt: return "DropOwnedStmt"; break; + case T_ReassignOwnedStmt: return "ReassignOwnedStmt"; break; + case T_CompositeTypeStmt: return "CompositeTypeStmt"; break; + case T_CreateEnumStmt: return "CreateEnumStmt"; break; + case T_CreateRangeStmt: return "CreateRangeStmt"; break; + case T_AlterEnumStmt: return "AlterEnumStmt"; break; + case T_AlterTSDictionaryStmt: return "AlterTSDictionaryStmt"; break; + case T_AlterTSConfigurationStmt: return "AlterTSConfigurationStmt"; break; + case T_CreateFdwStmt: return "CreateFdwStmt"; break; + case T_AlterFdwStmt: return "AlterFdwStmt"; break; + case T_CreateForeignServerStmt: return "CreateForeignServerStmt"; break; + case T_AlterForeignServerStmt: return "AlterForeignServerStmt"; break; + case T_CreateUserMappingStmt: return "CreateUserMappingStmt"; break; + case T_AlterUserMappingStmt: return "AlterUserMappingStmt"; break; + case T_DropUserMappingStmt: return "DropUserMappingStmt"; break; + case T_AlterTableSpaceOptionsStmt: return "AlterTableSpaceOptionsStmt"; break; + case T_AlterTableMoveAllStmt: return "AlterTableMoveAllStmt"; break; + case T_SecLabelStmt: return "SecLabelStmt"; break; + case T_CreateForeignTableStmt: return "CreateForeignTableStmt"; break; + case T_ImportForeignSchemaStmt: return "ImportForeignSchemaStmt"; break; + case T_CreateExtensionStmt: return "CreateExtensionStmt"; break; + case T_AlterExtensionStmt: return "AlterExtensionStmt"; break; + case T_AlterExtensionContentsStmt: return "AlterExtensionContentsStmt"; break; + case T_CreateEventTrigStmt: return "CreateEventTrigStmt"; break; + case T_AlterEventTrigStmt: return "AlterEventTrigStmt"; break; + case T_RefreshMatViewStmt: return "RefreshMatViewStmt"; break; + case T_ReplicaIdentityStmt: return "ReplicaIdentityStmt"; break; + case T_AlterSystemStmt: return "AlterSystemStmt"; break; + case T_CreatePolicyStmt: return "CreatePolicyStmt"; break; + case T_AlterPolicyStmt: return "AlterPolicyStmt"; break; + case T_CreateTransformStmt: return "CreateTransformStmt"; break; + case T_CreateAmStmt: return "CreateAmStmt"; break; + case T_CreatePublicationStmt: return "CreatePublicationStmt"; break; + case T_AlterPublicationStmt: return "AlterPublicationStmt"; break; + case T_CreateSubscriptionStmt: return "CreateSubscriptionStmt"; break; + case T_AlterSubscriptionStmt: return "AlterSubscriptionStmt"; break; + case T_DropSubscriptionStmt: return "DropSubscriptionStmt"; break; + case T_CreateStatsStmt: return "CreateStatsStmt"; break; + case T_AlterCollationStmt: return "AlterCollationStmt"; break; + case T_CallStmt: return "CallStmt"; break; + case T_AlterStatsStmt: return "AlterStatsStmt"; break; + case T_A_Expr: return "A_Expr"; break; + case T_ColumnRef: return "ColumnRef"; break; + case T_ParamRef: return "ParamRef"; break; + case T_A_Const: return "A_Const"; break; + case T_FuncCall: return "FuncCall"; break; + case T_A_Star: return "A_Star"; break; + case T_A_Indices: return "A_Indices"; break; + case T_A_Indirection: return "A_Indirection"; break; + case T_A_ArrayExpr: return "A_ArrayExpr"; break; + case T_ResTarget: return "ResTarget"; break; + case T_MultiAssignRef: return "MultiAssignRef"; break; + case T_TypeCast: return "TypeCast"; break; + case T_CollateClause: return "CollateClause"; break; + case T_SortBy: return "SortBy"; break; + case T_WindowDef: return "WindowDef"; break; + case T_RangeSubselect: return "RangeSubselect"; break; + case T_RangeFunction: return "RangeFunction"; break; + case T_RangeTableSample: return "RangeTableSample"; break; + case T_RangeTableFunc: return "RangeTableFunc"; break; + case T_RangeTableFuncCol: return "RangeTableFuncCol"; break; + case T_TypeName: return "TypeName"; break; + case T_ColumnDef: return "ColumnDef"; break; + case T_IndexElem: return "IndexElem"; break; + case T_StatsElem: return "StatsElem"; break; + case T_Constraint: return "Constraint"; break; + case T_DefElem: return "DefElem"; break; + case T_RangeTblEntry: return "RangeTblEntry"; break; + case T_RangeTblFunction: return "RangeTblFunction"; break; + case T_TableSampleClause: return "TableSampleClause"; break; + case T_WithCheckOption: return "WithCheckOption"; break; + case T_SortGroupClause: return "SortGroupClause"; break; + case T_GroupingSet: return "GroupingSet"; break; + case T_WindowClause: return "WindowClause"; break; + case T_ObjectWithArgs: return "ObjectWithArgs"; break; + case T_AccessPriv: return "AccessPriv"; break; + case T_CreateOpClassItem: return "CreateOpClassItem"; break; + case T_TableLikeClause: return "TableLikeClause"; break; + case T_FunctionParameter: return "FunctionParameter"; break; + case T_LockingClause: return "LockingClause"; break; + case T_RowMarkClause: return "RowMarkClause"; break; + case T_XmlSerialize: return "XmlSerialize"; break; + case T_WithClause: return "WithClause"; break; + case T_InferClause: return "InferClause"; break; + case T_OnConflictClause: return "OnConflictClause"; break; + case T_CTESearchClause: return "CTESearchClause"; break; + case T_CTECycleClause: return "CTECycleClause"; break; + case T_CommonTableExpr: return "CommonTableExpr"; break; + case T_RoleSpec: return "RoleSpec"; break; + case T_TriggerTransition: return "TriggerTransition"; break; + case T_PartitionElem: return "PartitionElem"; break; + case T_PartitionSpec: return "PartitionSpec"; break; + case T_PartitionBoundSpec: return "PartitionBoundSpec"; break; + case T_PartitionRangeDatum: return "PartitionRangeDatum"; break; + case T_PartitionCmd: return "PartitionCmd"; break; + case T_VacuumRelation: return "VacuumRelation"; break; + case T_PublicationObjSpec: return "PublicationObjSpec"; break; + case T_PublicationTable: return "PublicationTable"; break; + case T_IdentifySystemCmd: return "IdentifySystemCmd"; break; + case T_BaseBackupCmd: return "BaseBackupCmd"; break; + case T_CreateReplicationSlotCmd: return "CreateReplicationSlotCmd"; break; + case T_DropReplicationSlotCmd: return "DropReplicationSlotCmd"; break; + case T_ReadReplicationSlotCmd: return "ReadReplicationSlotCmd"; break; + case T_StartReplicationCmd: return "StartReplicationCmd"; break; + case T_TimeLineHistoryCmd: return "TimeLineHistoryCmd"; break; + case T_TriggerData: return "TriggerData"; break; + case T_EventTriggerData: return "EventTriggerData"; break; + case T_ReturnSetInfo: return "ReturnSetInfo"; break; + case T_WindowObjectData: return "WindowObjectData"; break; + case T_TIDBitmap: return "TIDBitmap"; break; + case T_InlineCodeBlock: return "InlineCodeBlock"; break; + case T_FdwRoutine: return "FdwRoutine"; break; + case T_IndexAmRoutine: return "IndexAmRoutine"; break; + case T_TableAmRoutine: return "TableAmRoutine"; break; + case T_TsmRoutine: return "TsmRoutine"; break; + case T_ForeignKeyCacheInfo: return "ForeignKeyCacheInfo"; break; + case T_CallContext: return "CallContext"; break; + case T_SupportRequestSimplify: return "SupportRequestSimplify"; break; + case T_SupportRequestSelectivity: return "SupportRequestSelectivity"; break; + case T_SupportRequestCost: return "SupportRequestCost"; break; + case T_SupportRequestRows: return "SupportRequestRows"; break; + case T_SupportRequestIndexCondition: return "SupportRequestIndexCondition"; break; + default: + break; + } + return "UNRECOGNIZED NodeTag"; +} + +static char * +accesstype_to_string(ObjectAccessType access, int subId) +{ + const char *type; + + switch (access) + { + case OAT_POST_CREATE: + type = "create"; + break; + case OAT_DROP: + type = "drop"; + break; + case OAT_POST_ALTER: + type = "alter"; + break; + case OAT_NAMESPACE_SEARCH: + type = "namespace search"; + break; + case OAT_FUNCTION_EXECUTE: + type = "execute"; + break; + case OAT_TRUNCATE: + type = "truncate"; + break; + default: + type = "UNRECOGNIZED ObjectAccessType"; + } + + if (subId & ACL_SET_VALUE) + return psprintf("%s (set)", type); + if (subId & ACL_ALTER_SYSTEM) + return psprintf("%s (alter system set)", type); + + return psprintf("%s (subId=%d)", type, subId); +} + +static char * +accesstype_arg_to_string(ObjectAccessType access, void *arg) +{ + if (arg == NULL) + return pstrdup("extra info null"); + + switch (access) + { + case OAT_POST_CREATE: + { + ObjectAccessPostCreate *pc_arg = (ObjectAccessPostCreate *)arg; + return pstrdup(pc_arg->is_internal ? "internal" : "explicit"); + } + break; + case OAT_DROP: + { + ObjectAccessDrop *drop_arg = (ObjectAccessDrop *)arg; + + return psprintf("%s%s%s%s%s%s", + ((drop_arg->dropflags & PERFORM_DELETION_INTERNAL) + ? "internal action," : ""), + ((drop_arg->dropflags & PERFORM_DELETION_INTERNAL) + ? "concurrent drop," : ""), + ((drop_arg->dropflags & PERFORM_DELETION_INTERNAL) + ? "suppress notices," : ""), + ((drop_arg->dropflags & PERFORM_DELETION_INTERNAL) + ? "keep original object," : ""), + ((drop_arg->dropflags & PERFORM_DELETION_INTERNAL) + ? "keep extensions," : ""), + ((drop_arg->dropflags & PERFORM_DELETION_INTERNAL) + ? "normal concurrent drop," : "")); + } + break; + case OAT_POST_ALTER: + { + ObjectAccessPostAlter *pa_arg = (ObjectAccessPostAlter*)arg; + + return psprintf("%s %s auxiliary object", + (pa_arg->is_internal ? "internal" : "explicit"), + (OidIsValid(pa_arg->auxiliary_id) ? "with" : "without")); + } + break; + case OAT_NAMESPACE_SEARCH: + { + ObjectAccessNamespaceSearch *ns_arg = (ObjectAccessNamespaceSearch *)arg; + + return psprintf("%s, %s", + (ns_arg->ereport_on_violation ? "report on violation" : "no report on violation"), + (ns_arg->result ? "allowed" : "denied")); + } + break; + case OAT_TRUNCATE: + case OAT_FUNCTION_EXECUTE: + /* hook takes no arg. */ + return pstrdup("unexpected extra info pointer received"); + default: + return pstrdup("cannot parse extra info for unrecognized access type"); + } + + return pstrdup("unknown"); +} diff --git a/src/test/modules/test_oat_hooks/test_oat_hooks.conf b/src/test/modules/test_oat_hooks/test_oat_hooks.conf new file mode 100644 index 00000000000..a44cbdd4a4f --- /dev/null +++ b/src/test/modules/test_oat_hooks/test_oat_hooks.conf @@ -0,0 +1 @@ +shared_preload_libraries = test_oat_hooks