diff --git a/contrib/sepgsql/expected/label.out b/contrib/sepgsql/expected/label.out
index 0a15f279f8..18bdf811e8 100644
--- a/contrib/sepgsql/expected/label.out
+++ b/contrib/sepgsql/expected/label.out
@@ -131,23 +131,40 @@ SELECT sepgsql_getcon(); -- confirm client privilege
unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
(1 row)
+SET sepgsql.debug_audit = true;
+SET client_min_messages = log;
SELECT f1(); -- normal procedure
+LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=unconfined_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function f1()"
+LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function sepgsql_getcon()"
+CONTEXT: SQL function "f1" statement 1
f1
-----------------------------------------------------
unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
(1 row)
SELECT f2(); -- trusted procedure
+LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=system_u:object_r:sepgsql_trusted_proc_exec_t:s0 tclass=db_procedure name="function f2()"
+LOG: SELinux: allowed { entrypoint } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=system_u:object_r:sepgsql_trusted_proc_exec_t:s0 tclass=db_procedure name="function f2()"
+LOG: SELinux: allowed { transition } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=unconfined_u:unconfined_r:sepgsql_trusted_proc_t:s0 tclass=process
+LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_trusted_proc_t:s0 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function sepgsql_getcon()"
+CONTEXT: SQL function "f2" statement 1
f2
-----------------------------------------------------
unconfined_u:unconfined_r:sepgsql_trusted_proc_t:s0
(1 row)
SELECT f3(); -- trusted procedure that raises an error
+LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=system_u:object_r:sepgsql_trusted_proc_exec_t:s0 tclass=db_procedure name="function f3()"
+LOG: SELinux: allowed { entrypoint } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=system_u:object_r:sepgsql_trusted_proc_exec_t:s0 tclass=db_procedure name="function f3()"
+LOG: SELinux: allowed { transition } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=unconfined_u:unconfined_r:sepgsql_trusted_proc_t:s0 tclass=process
ERROR: an exception from f3()
SELECT f4(); -- failed on domain transition
+LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=system_u:object_r:sepgsql_nosuch_trusted_proc_exec_t:s0 tclass=db_procedure name="function f4()"
+LOG: SELinux: allowed { entrypoint } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=system_u:object_r:sepgsql_nosuch_trusted_proc_exec_t:s0 tclass=db_procedure name="function f4()"
+LOG: SELinux: denied { transition } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=unconfined_u:unconfined_r:sepgsql_regtest_nosuch_t:s0 tclass=process
ERROR: SELinux: security policy violation
SELECT sepgsql_getcon(); -- client's label must be restored
+LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function sepgsql_getcon()"
sepgsql_getcon
-----------------------------------------------------
unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
diff --git a/contrib/sepgsql/expected/misc.out b/contrib/sepgsql/expected/misc.out
index 329852c574..26d9c188c2 100644
--- a/contrib/sepgsql/expected/misc.out
+++ b/contrib/sepgsql/expected/misc.out
@@ -3,3 +3,70 @@
--
LOAD '$libdir/sepgsql'; -- failed
ERROR: SELinux: LOAD is not permitted
+--
+-- Permissions to execute functions
+--
+CREATE TABLE t1 (x int, y text);
+INSERT INTO t1 (SELECT x, md5(x::text) FROM generate_series(1,100) x);
+SET sepgsql.debug_audit = on;
+SET client_min_messages = log;
+-- regular function and operators
+SELECT * FROM t1 WHERE x > 50 AND y like '%64%';
+LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1"
+LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1 column x"
+LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1 column y"
+LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function int4gt(integer,integer)"
+LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function textlike(text,text)"
+ x | y
+-----+----------------------------------
+ 77 | 28dd2c7955ce926456240b2ff0100bde
+ 89 | 7647966b7343c29048673252e490f736
+ 90 | 8613985ec49eb8f757ae6439e879bb2a
+ 91 | 54229abfcfa5649e7003b83dd4755294
+ 99 | ac627ab1ccbdb62ec96e702f07f6425b
+ 100 | f899139df5e1059396431415e770c6dd
+(6 rows)
+
+-- aggregate function
+SELECT MIN(x), AVG(x) FROM t1;
+LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1"
+LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1 column x"
+LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function avg(integer)"
+LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function int4_avg_accum(bigint[],integer)"
+LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function int8_avg(bigint[])"
+LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function min(integer)"
+LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function int4smaller(integer,integer)"
+ min | avg
+-----+---------------------
+ 1 | 50.5000000000000000
+(1 row)
+
+-- window function
+SELECT row_number() OVER (order by x), * FROM t1 WHERE y like '%86%';
+LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1"
+LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1 column x"
+LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1 column y"
+LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function row_number()"
+LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function textlike(text,text)"
+ row_number | x | y
+------------+----+----------------------------------
+ 1 | 2 | c81e728d9d4c2f636f067f89cc14862c
+ 2 | 17 | 70efdf2ec9b086079795c442636b55fb
+ 3 | 22 | b6d767d2f8ed5d21a44b0e5886680cb9
+ 4 | 27 | 02e74f10e0327ad868d138f2b4fdd6f0
+ 5 | 33 | 182be0c5cdcd5072bb1864cdee4d3d6e
+ 6 | 43 | 17e62166fc8586dfa4d1bc0e1742c08b
+ 7 | 54 | a684eceee76fc522773286a895bc8436
+ 8 | 73 | d2ddea18f00665ce8623e36bd4e3c7c5
+ 9 | 76 | fbd7939d674997cdb4692d34de8633c4
+ 10 | 89 | 7647966b7343c29048673252e490f736
+ 11 | 90 | 8613985ec49eb8f757ae6439e879bb2a
+ 12 | 94 | f4b9ec30ad9f68f89b29639786cb62ef
+(12 rows)
+
+RESET sepgsql.debug_audit;
+RESET client_min_messages;
+--
+-- Cleanup
+--
+DROP TABLE IF EXISTS t1 CASCADE;
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 5faa1ea797..04c5120890 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -255,6 +255,13 @@ sepgsql_object_access(ObjectAccessType access,
}
break;
+ case OAT_FUNCTION_EXECUTE:
+ {
+ Assert(classId == ProcedureRelationId);
+ sepgsql_proc_execute(objectId);
+ }
+ break;
+
default:
elog(ERROR, "unexpected object access type: %d", (int) access);
break;
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c
index a5bdde3490..6146399e80 100644
--- a/contrib/sepgsql/label.c
+++ b/contrib/sepgsql/label.c
@@ -303,7 +303,8 @@ sepgsql_needs_fmgr_hook(Oid functionId)
object.objectSubId = 0;
if (!sepgsql_avc_check_perms(&object,
SEPG_CLASS_DB_PROCEDURE,
- SEPG_DB_PROCEDURE__EXECUTE,
+ SEPG_DB_PROCEDURE__EXECUTE |
+ SEPG_DB_PROCEDURE__ENTRYPOINT,
SEPGSQL_AVC_NOAUDIT, false))
return true;
@@ -347,13 +348,31 @@ sepgsql_fmgr_hook(FmgrHookEventType event,
* process:transition permission between old and new label,
* when user tries to switch security label of the client on
* execution of trusted procedure.
+ *
+ * Also, db_procedure:entrypoint permission should be checked
+ * whether this procedure can perform as an entrypoint of the
+ * trusted procedure, or not.
+ * Note that db_procedure:execute permission shall be checked
+ * individually.
*/
if (stack->new_label)
+ {
+ ObjectAddress object;
+
+ object.classId = ProcedureRelationId;
+ object.objectId = flinfo->fn_oid;
+ object.objectSubId = 0;
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_PROCEDURE,
+ SEPG_DB_PROCEDURE__ENTRYPOINT,
+ getObjectDescription(&object),
+ true);
+
sepgsql_avc_check_perms_label(stack->new_label,
SEPG_CLASS_PROCESS,
SEPG_PROCESS__TRANSITION,
NULL, true);
-
+ }
*private = PointerGetDatum(stack);
}
Assert(!stack->old_label);
diff --git a/contrib/sepgsql/proc.c b/contrib/sepgsql/proc.c
index c0b0f6aeeb..fe33119118 100644
--- a/contrib/sepgsql/proc.c
+++ b/contrib/sepgsql/proc.c
@@ -307,3 +307,29 @@ sepgsql_proc_setattr(Oid functionId)
systable_endscan(sscan);
heap_close(rel, AccessShareLock);
}
+
+/*
+ * sepgsql_proc_execute
+ *
+ * It checks privileges to execute the supplied function
+ */
+void
+sepgsql_proc_execute(Oid functionId)
+{
+ ObjectAddress object;
+ char *audit_name;
+
+ /*
+ * check db_procedure:{execute} permission
+ */
+ object.classId = ProcedureRelationId;
+ object.objectId = functionId;
+ object.objectSubId = 0;
+ audit_name = getObjectDescription(&object);
+ sepgsql_avc_check_perms(&object,
+ SEPG_CLASS_DB_PROCEDURE,
+ SEPG_DB_PROCEDURE__EXECUTE,
+ audit_name,
+ true);
+ pfree(audit_name);
+}
diff --git a/contrib/sepgsql/sepgsql-regtest.te b/contrib/sepgsql/sepgsql-regtest.te
index 823384ecc6..8727523ca5 100644
--- a/contrib/sepgsql/sepgsql-regtest.te
+++ b/contrib/sepgsql/sepgsql-regtest.te
@@ -1,4 +1,4 @@
-policy_module(sepgsql-regtest, 1.06)
+policy_module(sepgsql-regtest, 1.07)
gen_require(`
all_userspace_class_perms
@@ -172,25 +172,14 @@ optional_policy(`
#
# Rule to execute original trusted procedures
#
-# XXX - sepgsql_client_type contains any valid client types, so we allow
-# them to execute the original trusted procedure at once.
+# These rules intends to allow any valid client types to launch trusted-
+# procedures (including ones causes domain transition to invalid domain)
+# being labeled as sepgsql_regtest_trusted_proc_exec_t and
+# sepgsql_nosuch_trusted_proc_exec_t.
#
optional_policy(`
gen_require(`
attribute sepgsql_client_type;
')
- allow sepgsql_client_type { sepgsql_regtest_trusted_proc_exec_t sepgsql_nosuch_trusted_proc_exec_t }:db_procedure { getattr execute };
-
- # These rules intends sepgsql_regtest_user_t domain to translate
- # sepgsql_regtest_dba_t on execution of procedures labeled as
- # sepgsql_regtest_trusted_proc_exec_t.
- #
-# allow sepgsql_client_type sepgsql_regtest_trusted_proc_exec_t:db_procedure { getattr execute };
-
- # These rules intends sepgsql_regtest_user_t domain to translate
- # sepgsql_regtest_nosuch_t on execution of procedures labeled as
- # sepgsql_nosuch_trusted_proc_exec_t, without permissions to
- # translate to sepgsql_nosuch_trusted_proc_exec_t.
- #
-# allow sepgsql_client_type sepgsql_nosuch_trusted_proc_exec_t:db_procedure { getattr execute install };
+ allow sepgsql_client_type { sepgsql_regtest_trusted_proc_exec_t sepgsql_nosuch_trusted_proc_exec_t }:db_procedure { getattr execute entrypoint };
')
diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h
index 37d7455fd2..7359f1be4a 100644
--- a/contrib/sepgsql/sepgsql.h
+++ b/contrib/sepgsql/sepgsql.h
@@ -328,5 +328,6 @@ extern void sepgsql_proc_post_create(Oid functionId);
extern void sepgsql_proc_drop(Oid functionId);
extern void sepgsql_proc_relabel(Oid functionId, const char *seclabel);
extern void sepgsql_proc_setattr(Oid functionId);
+extern void sepgsql_proc_execute(Oid functionId);
#endif /* SEPGSQL_H */
diff --git a/contrib/sepgsql/sql/label.sql b/contrib/sepgsql/sql/label.sql
index 6201cd7721..7a05c248eb 100644
--- a/contrib/sepgsql/sql/label.sql
+++ b/contrib/sepgsql/sql/label.sql
@@ -97,6 +97,8 @@ SECURITY LABEL ON COLUMN t2.b
-- Tests for Trusted Procedures
--
-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
+SET sepgsql.debug_audit = true;
+SET client_min_messages = log;
SELECT f1(); -- normal procedure
SELECT f2(); -- trusted procedure
SELECT f3(); -- trusted procedure that raises an error
diff --git a/contrib/sepgsql/sql/misc.sql b/contrib/sepgsql/sql/misc.sql
index a46d8a6b5c..c277711781 100644
--- a/contrib/sepgsql/sql/misc.sql
+++ b/contrib/sepgsql/sql/misc.sql
@@ -3,3 +3,28 @@
--
LOAD '$libdir/sepgsql'; -- failed
+
+--
+-- Permissions to execute functions
+--
+CREATE TABLE t1 (x int, y text);
+INSERT INTO t1 (SELECT x, md5(x::text) FROM generate_series(1,100) x);
+
+SET sepgsql.debug_audit = on;
+SET client_min_messages = log;
+
+-- regular function and operators
+SELECT * FROM t1 WHERE x > 50 AND y like '%64%';
+
+-- aggregate function
+SELECT MIN(x), AVG(x) FROM t1;
+
+-- window function
+SELECT row_number() OVER (order by x), * FROM t1 WHERE y like '%86%';
+
+RESET sepgsql.debug_audit;
+RESET client_min_messages;
+--
+-- Cleanup
+--
+DROP TABLE IF EXISTS t1 CASCADE;
diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml
index 0a2ee86a11..2cdbe9de43 100644
--- a/doc/src/sgml/sepgsql.sgml
+++ b/doc/src/sgml/sepgsql.sgml
@@ -393,8 +393,11 @@ UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100;
- For functions, db_procedure:{execute}> is defined, but is not
- checked in this version.
+ For functions, db_procedure:{execute}> will be checked when
+ user tries to execute a function as a part of query, or using fast-path
+ invocation. If this function is a trusted procedure, it also checks
+ db_procedure:{entrypoint}> permission to check whether it
+ can perform as entrypoint of trusted procedure.
diff --git a/src/backend/catalog/objectaccess.c b/src/backend/catalog/objectaccess.c
index ad12d74f52..87158e34e2 100644
--- a/src/backend/catalog/objectaccess.c
+++ b/src/backend/catalog/objectaccess.c
@@ -12,6 +12,7 @@
#include "catalog/objectaccess.h"
#include "catalog/pg_namespace.h"
+#include "catalog/pg_proc.h"
/*
* Hook on object accesses. This is intended as infrastructure for security
@@ -109,3 +110,19 @@ RunNamespaceSearchHook(Oid objectId, bool ereport_on_violation)
return ns_arg.result;
}
+
+/*
+ * RunFunctionExecuteHook
+ *
+ * It is entrypoint of OAT_FUNCTION_EXECUTE event
+ */
+void
+RunFunctionExecuteHook(Oid objectId)
+{
+ /* caller should check, but just in case... */
+ Assert(object_access_hook != NULL);
+
+ (*object_access_hook)(OAT_FUNCTION_EXECUTE,
+ ProcedureRelationId, objectId, 0,
+ NULL);
+}
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 62d27a7574..330d889eba 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -39,6 +39,7 @@
#include "access/htup_details.h"
#include "access/nbtree.h"
#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_type.h"
#include "commands/typecmds.h"
#include "executor/execdebug.h"
@@ -1289,6 +1290,7 @@ init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid));
+ InvokeFunctionExecuteHook(foid);
/*
* Safety check on nargs. Under normal circumstances this should never
@@ -4223,6 +4225,7 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_PROC,
get_func_name(acoerce->elemfuncid));
+ InvokeFunctionExecuteHook(acoerce->elemfuncid);
/* Set up the primary fmgr lookup information */
fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc),
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 7efc4905cf..c741131b25 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -79,6 +79,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
@@ -1625,6 +1626,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_PROC,
get_func_name(aggref->aggfnoid));
+ InvokeFunctionExecuteHook(aggref->aggfnoid);
peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
@@ -1647,6 +1649,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_PROC,
get_func_name(transfn_oid));
+ InvokeFunctionExecuteHook(transfn_oid);
if (OidIsValid(finalfn_oid))
{
aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner,
@@ -1654,6 +1657,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_PROC,
get_func_name(finalfn_oid));
+ InvokeFunctionExecuteHook(finalfn_oid);
}
}
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 3bc42baed1..d9f0e79d10 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -34,6 +34,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
@@ -1559,6 +1560,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_PROC,
get_func_name(wfunc->winfnoid));
+ InvokeFunctionExecuteHook(wfunc->winfnoid);
/* Fill in the perfuncstate data */
perfuncstate->wfuncstate = wfuncstate;
@@ -1767,6 +1769,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_PROC,
get_func_name(transfn_oid));
+ InvokeFunctionExecuteHook(transfn_oid);
if (OidIsValid(finalfn_oid))
{
aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner,
@@ -1774,6 +1777,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_PROC,
get_func_name(finalfn_oid));
+ InvokeFunctionExecuteHook(finalfn_oid);
}
}
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index 016e7d9ec6..bd0801c0bf 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -362,6 +362,7 @@ HandleFunctionRequest(StringInfo msgBuf)
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_PROC,
get_func_name(fid));
+ InvokeFunctionExecuteHook(fid);
/*
* Prepare function call info block and insert arguments.
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index a34c4c7474..f4e464e566 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -31,6 +31,12 @@
* a particular namespace. This event is equivalent to usage permission
* on a schema under the default access control mechanism.
*
+ * OAT_FUNCTION_EXECUTE should be invoked prior to function execution.
+ * This event is almost equivalent to execute permission on functions,
+ * except for the case when execute permission is checked during object
+ * creation or altering, because OAT_POST_CREATE or OAT_POST_ALTER are
+ * sufficient for extensions to track these kind of checks.
+ *
* Other types may be added in the future.
*/
typedef enum ObjectAccessType
@@ -39,6 +45,7 @@ typedef enum ObjectAccessType
OAT_DROP,
OAT_POST_ALTER,
OAT_NAMESPACE_SEARCH,
+ OAT_FUNCTION_EXECUTE,
} ObjectAccessType;
/*
@@ -129,6 +136,7 @@ extern void RunObjectDropHook(Oid classId, Oid objectId, int subId,
extern void RunObjectPostAlterHook(Oid classId, Oid objectId, int subId,
Oid auxiliaryId, bool is_internal);
extern bool RunNamespaceSearchHook(Oid objectId, bool ereport_on_volation);
+extern void RunFunctionExecuteHook(Oid objectId);
/*
* The following macros are wrappers around the functions above; these should
@@ -170,4 +178,10 @@ extern bool RunNamespaceSearchHook(Oid objectId, bool ereport_on_volation);
? true \
: RunNamespaceSearchHook((objectId), (ereport_on_violation)))
+#define InvokeFunctionExecuteHook(objectId) \
+ do { \
+ if (object_access_hook) \
+ RunFunctionExecuteHook(objectId); \
+ } while(0)
+
#endif /* OBJECTACCESS_H */