Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit f8a54e9

Browse files
committed
sepgsql: Enforce db_procedure:{execute} permission.
To do this, we add an additional object access hook type, OAT_FUNCTION_EXECUTE. KaiGai Kohei
1 parent d017bf4 commit f8a54e9

File tree

16 files changed

+220
-21
lines changed

16 files changed

+220
-21
lines changed

contrib/sepgsql/expected/label.out

+17
Original file line numberDiff line numberDiff line change
@@ -131,23 +131,40 @@ SELECT sepgsql_getcon(); -- confirm client privilege
131131
unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
132132
(1 row)
133133

134+
SET sepgsql.debug_audit = true;
135+
SET client_min_messages = log;
134136
SELECT f1(); -- normal procedure
137+
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()"
138+
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()"
139+
CONTEXT: SQL function "f1" statement 1
135140
f1
136141
-----------------------------------------------------
137142
unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
138143
(1 row)
139144

140145
SELECT f2(); -- trusted procedure
146+
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()"
147+
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()"
148+
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
149+
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()"
150+
CONTEXT: SQL function "f2" statement 1
141151
f2
142152
-----------------------------------------------------
143153
unconfined_u:unconfined_r:sepgsql_trusted_proc_t:s0
144154
(1 row)
145155

146156
SELECT f3(); -- trusted procedure that raises an error
157+
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()"
158+
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()"
159+
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
147160
ERROR: an exception from f3()
148161
SELECT f4(); -- failed on domain transition
162+
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()"
163+
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()"
164+
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
149165
ERROR: SELinux: security policy violation
150166
SELECT sepgsql_getcon(); -- client's label must be restored
167+
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()"
151168
sepgsql_getcon
152169
-----------------------------------------------------
153170
unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0

contrib/sepgsql/expected/misc.out

+67
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,70 @@
33
--
44
LOAD '$libdir/sepgsql'; -- failed
55
ERROR: SELinux: LOAD is not permitted
6+
--
7+
-- Permissions to execute functions
8+
--
9+
CREATE TABLE t1 (x int, y text);
10+
INSERT INTO t1 (SELECT x, md5(x::text) FROM generate_series(1,100) x);
11+
SET sepgsql.debug_audit = on;
12+
SET client_min_messages = log;
13+
-- regular function and operators
14+
SELECT * FROM t1 WHERE x > 50 AND y like '%64%';
15+
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"
16+
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"
17+
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"
18+
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)"
19+
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)"
20+
x | y
21+
-----+----------------------------------
22+
77 | 28dd2c7955ce926456240b2ff0100bde
23+
89 | 7647966b7343c29048673252e490f736
24+
90 | 8613985ec49eb8f757ae6439e879bb2a
25+
91 | 54229abfcfa5649e7003b83dd4755294
26+
99 | ac627ab1ccbdb62ec96e702f07f6425b
27+
100 | f899139df5e1059396431415e770c6dd
28+
(6 rows)
29+
30+
-- aggregate function
31+
SELECT MIN(x), AVG(x) FROM t1;
32+
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"
33+
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"
34+
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)"
35+
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)"
36+
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[])"
37+
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)"
38+
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)"
39+
min | avg
40+
-----+---------------------
41+
1 | 50.5000000000000000
42+
(1 row)
43+
44+
-- window function
45+
SELECT row_number() OVER (order by x), * FROM t1 WHERE y like '%86%';
46+
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"
47+
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"
48+
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"
49+
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()"
50+
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)"
51+
row_number | x | y
52+
------------+----+----------------------------------
53+
1 | 2 | c81e728d9d4c2f636f067f89cc14862c
54+
2 | 17 | 70efdf2ec9b086079795c442636b55fb
55+
3 | 22 | b6d767d2f8ed5d21a44b0e5886680cb9
56+
4 | 27 | 02e74f10e0327ad868d138f2b4fdd6f0
57+
5 | 33 | 182be0c5cdcd5072bb1864cdee4d3d6e
58+
6 | 43 | 17e62166fc8586dfa4d1bc0e1742c08b
59+
7 | 54 | a684eceee76fc522773286a895bc8436
60+
8 | 73 | d2ddea18f00665ce8623e36bd4e3c7c5
61+
9 | 76 | fbd7939d674997cdb4692d34de8633c4
62+
10 | 89 | 7647966b7343c29048673252e490f736
63+
11 | 90 | 8613985ec49eb8f757ae6439e879bb2a
64+
12 | 94 | f4b9ec30ad9f68f89b29639786cb62ef
65+
(12 rows)
66+
67+
RESET sepgsql.debug_audit;
68+
RESET client_min_messages;
69+
--
70+
-- Cleanup
71+
--
72+
DROP TABLE IF EXISTS t1 CASCADE;

contrib/sepgsql/hooks.c

+7
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,13 @@ sepgsql_object_access(ObjectAccessType access,
255255
}
256256
break;
257257

258+
case OAT_FUNCTION_EXECUTE:
259+
{
260+
Assert(classId == ProcedureRelationId);
261+
sepgsql_proc_execute(objectId);
262+
}
263+
break;
264+
258265
default:
259266
elog(ERROR, "unexpected object access type: %d", (int) access);
260267
break;

contrib/sepgsql/label.c

+21-2
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,8 @@ sepgsql_needs_fmgr_hook(Oid functionId)
303303
object.objectSubId = 0;
304304
if (!sepgsql_avc_check_perms(&object,
305305
SEPG_CLASS_DB_PROCEDURE,
306-
SEPG_DB_PROCEDURE__EXECUTE,
306+
SEPG_DB_PROCEDURE__EXECUTE |
307+
SEPG_DB_PROCEDURE__ENTRYPOINT,
307308
SEPGSQL_AVC_NOAUDIT, false))
308309
return true;
309310

@@ -347,13 +348,31 @@ sepgsql_fmgr_hook(FmgrHookEventType event,
347348
* process:transition permission between old and new label,
348349
* when user tries to switch security label of the client on
349350
* execution of trusted procedure.
351+
*
352+
* Also, db_procedure:entrypoint permission should be checked
353+
* whether this procedure can perform as an entrypoint of the
354+
* trusted procedure, or not.
355+
* Note that db_procedure:execute permission shall be checked
356+
* individually.
350357
*/
351358
if (stack->new_label)
359+
{
360+
ObjectAddress object;
361+
362+
object.classId = ProcedureRelationId;
363+
object.objectId = flinfo->fn_oid;
364+
object.objectSubId = 0;
365+
sepgsql_avc_check_perms(&object,
366+
SEPG_CLASS_DB_PROCEDURE,
367+
SEPG_DB_PROCEDURE__ENTRYPOINT,
368+
getObjectDescription(&object),
369+
true);
370+
352371
sepgsql_avc_check_perms_label(stack->new_label,
353372
SEPG_CLASS_PROCESS,
354373
SEPG_PROCESS__TRANSITION,
355374
NULL, true);
356-
375+
}
357376
*private = PointerGetDatum(stack);
358377
}
359378
Assert(!stack->old_label);

contrib/sepgsql/proc.c

+26
Original file line numberDiff line numberDiff line change
@@ -307,3 +307,29 @@ sepgsql_proc_setattr(Oid functionId)
307307
systable_endscan(sscan);
308308
heap_close(rel, AccessShareLock);
309309
}
310+
311+
/*
312+
* sepgsql_proc_execute
313+
*
314+
* It checks privileges to execute the supplied function
315+
*/
316+
void
317+
sepgsql_proc_execute(Oid functionId)
318+
{
319+
ObjectAddress object;
320+
char *audit_name;
321+
322+
/*
323+
* check db_procedure:{execute} permission
324+
*/
325+
object.classId = ProcedureRelationId;
326+
object.objectId = functionId;
327+
object.objectSubId = 0;
328+
audit_name = getObjectDescription(&object);
329+
sepgsql_avc_check_perms(&object,
330+
SEPG_CLASS_DB_PROCEDURE,
331+
SEPG_DB_PROCEDURE__EXECUTE,
332+
audit_name,
333+
true);
334+
pfree(audit_name);
335+
}

contrib/sepgsql/sepgsql-regtest.te

+6-17
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
policy_module(sepgsql-regtest, 1.06)
1+
policy_module(sepgsql-regtest, 1.07)
22

33
gen_require(`
44
all_userspace_class_perms
@@ -172,25 +172,14 @@ optional_policy(`
172172
#
173173
# Rule to execute original trusted procedures
174174
#
175-
# XXX - sepgsql_client_type contains any valid client types, so we allow
176-
# them to execute the original trusted procedure at once.
175+
# These rules intends to allow any valid client types to launch trusted-
176+
# procedures (including ones causes domain transition to invalid domain)
177+
# being labeled as sepgsql_regtest_trusted_proc_exec_t and
178+
# sepgsql_nosuch_trusted_proc_exec_t.
177179
#
178180
optional_policy(`
179181
gen_require(`
180182
attribute sepgsql_client_type;
181183
')
182-
allow sepgsql_client_type { sepgsql_regtest_trusted_proc_exec_t sepgsql_nosuch_trusted_proc_exec_t }:db_procedure { getattr execute };
183-
184-
# These rules intends sepgsql_regtest_user_t domain to translate
185-
# sepgsql_regtest_dba_t on execution of procedures labeled as
186-
# sepgsql_regtest_trusted_proc_exec_t.
187-
#
188-
# allow sepgsql_client_type sepgsql_regtest_trusted_proc_exec_t:db_procedure { getattr execute };
189-
190-
# These rules intends sepgsql_regtest_user_t domain to translate
191-
# sepgsql_regtest_nosuch_t on execution of procedures labeled as
192-
# sepgsql_nosuch_trusted_proc_exec_t, without permissions to
193-
# translate to sepgsql_nosuch_trusted_proc_exec_t.
194-
#
195-
# allow sepgsql_client_type sepgsql_nosuch_trusted_proc_exec_t:db_procedure { getattr execute install };
184+
allow sepgsql_client_type { sepgsql_regtest_trusted_proc_exec_t sepgsql_nosuch_trusted_proc_exec_t }:db_procedure { getattr execute entrypoint };
196185
')

contrib/sepgsql/sepgsql.h

+1
Original file line numberDiff line numberDiff line change
@@ -328,5 +328,6 @@ extern void sepgsql_proc_post_create(Oid functionId);
328328
extern void sepgsql_proc_drop(Oid functionId);
329329
extern void sepgsql_proc_relabel(Oid functionId, const char *seclabel);
330330
extern void sepgsql_proc_setattr(Oid functionId);
331+
extern void sepgsql_proc_execute(Oid functionId);
331332

332333
#endif /* SEPGSQL_H */

contrib/sepgsql/sql/label.sql

+2
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ SECURITY LABEL ON COLUMN t2.b
9797
-- Tests for Trusted Procedures
9898
--
9999
-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
100+
SET sepgsql.debug_audit = true;
101+
SET client_min_messages = log;
100102
SELECT f1(); -- normal procedure
101103
SELECT f2(); -- trusted procedure
102104
SELECT f3(); -- trusted procedure that raises an error

contrib/sepgsql/sql/misc.sql

+25
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,28 @@
33
--
44

55
LOAD '$libdir/sepgsql'; -- failed
6+
7+
--
8+
-- Permissions to execute functions
9+
--
10+
CREATE TABLE t1 (x int, y text);
11+
INSERT INTO t1 (SELECT x, md5(x::text) FROM generate_series(1,100) x);
12+
13+
SET sepgsql.debug_audit = on;
14+
SET client_min_messages = log;
15+
16+
-- regular function and operators
17+
SELECT * FROM t1 WHERE x > 50 AND y like '%64%';
18+
19+
-- aggregate function
20+
SELECT MIN(x), AVG(x) FROM t1;
21+
22+
-- window function
23+
SELECT row_number() OVER (order by x), * FROM t1 WHERE y like '%86%';
24+
25+
RESET sepgsql.debug_audit;
26+
RESET client_min_messages;
27+
--
28+
-- Cleanup
29+
--
30+
DROP TABLE IF EXISTS t1 CASCADE;

doc/src/sgml/sepgsql.sgml

+5-2
Original file line numberDiff line numberDiff line change
@@ -393,8 +393,11 @@ UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100;
393393
</para>
394394

395395
<para>
396-
For functions, <literal>db_procedure:{execute}</> is defined, but is not
397-
checked in this version.
396+
For functions, <literal>db_procedure:{execute}</> will be checked when
397+
user tries to execute a function as a part of query, or using fast-path
398+
invocation. If this function is a trusted procedure, it also checks
399+
<literal>db_procedure:{entrypoint}</> permission to check whether it
400+
can perform as entrypoint of trusted procedure.
398401
</para>
399402

400403
<para>

src/backend/catalog/objectaccess.c

+17
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "catalog/objectaccess.h"
1414
#include "catalog/pg_namespace.h"
15+
#include "catalog/pg_proc.h"
1516

1617
/*
1718
* Hook on object accesses. This is intended as infrastructure for security
@@ -109,3 +110,19 @@ RunNamespaceSearchHook(Oid objectId, bool ereport_on_violation)
109110

110111
return ns_arg.result;
111112
}
113+
114+
/*
115+
* RunFunctionExecuteHook
116+
*
117+
* It is entrypoint of OAT_FUNCTION_EXECUTE event
118+
*/
119+
void
120+
RunFunctionExecuteHook(Oid objectId)
121+
{
122+
/* caller should check, but just in case... */
123+
Assert(object_access_hook != NULL);
124+
125+
(*object_access_hook)(OAT_FUNCTION_EXECUTE,
126+
ProcedureRelationId, objectId, 0,
127+
NULL);
128+
}

src/backend/executor/execQual.c

+3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "access/htup_details.h"
4040
#include "access/nbtree.h"
4141
#include "access/tupconvert.h"
42+
#include "catalog/objectaccess.h"
4243
#include "catalog/pg_type.h"
4344
#include "commands/typecmds.h"
4445
#include "executor/execdebug.h"
@@ -1289,6 +1290,7 @@ init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
12891290
aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE);
12901291
if (aclresult != ACLCHECK_OK)
12911292
aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid));
1293+
InvokeFunctionExecuteHook(foid);
12921294

12931295
/*
12941296
* Safety check on nargs. Under normal circumstances this should never
@@ -4223,6 +4225,7 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
42234225
if (aclresult != ACLCHECK_OK)
42244226
aclcheck_error(aclresult, ACL_KIND_PROC,
42254227
get_func_name(acoerce->elemfuncid));
4228+
InvokeFunctionExecuteHook(acoerce->elemfuncid);
42264229

42274230
/* Set up the primary fmgr lookup information */
42284231
fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc),

src/backend/executor/nodeAgg.c

+4
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
#include "postgres.h"
8080

8181
#include "access/htup_details.h"
82+
#include "catalog/objectaccess.h"
8283
#include "catalog/pg_aggregate.h"
8384
#include "catalog/pg_proc.h"
8485
#include "catalog/pg_type.h"
@@ -1625,6 +1626,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
16251626
if (aclresult != ACLCHECK_OK)
16261627
aclcheck_error(aclresult, ACL_KIND_PROC,
16271628
get_func_name(aggref->aggfnoid));
1629+
InvokeFunctionExecuteHook(aggref->aggfnoid);
16281630

16291631
peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
16301632
peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
@@ -1647,13 +1649,15 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
16471649
if (aclresult != ACLCHECK_OK)
16481650
aclcheck_error(aclresult, ACL_KIND_PROC,
16491651
get_func_name(transfn_oid));
1652+
InvokeFunctionExecuteHook(transfn_oid);
16501653
if (OidIsValid(finalfn_oid))
16511654
{
16521655
aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner,
16531656
ACL_EXECUTE);
16541657
if (aclresult != ACLCHECK_OK)
16551658
aclcheck_error(aclresult, ACL_KIND_PROC,
16561659
get_func_name(finalfn_oid));
1660+
InvokeFunctionExecuteHook(finalfn_oid);
16571661
}
16581662
}
16591663

0 commit comments

Comments
 (0)