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 */