1 /* -------------------------------------------------------------------------
3 * contrib/sepgsql/hooks.c
5 * Entrypoints of the hooks in PostgreSQL, and dispatches the callbacks.
7 * Copyright (c) 2010-2024, PostgreSQL Global Development Group
9 * -------------------------------------------------------------------------
13 #include "catalog/dependency.h"
14 #include "catalog/objectaccess.h"
15 #include "catalog/pg_class.h"
16 #include "catalog/pg_database.h"
17 #include "catalog/pg_namespace.h"
18 #include "catalog/pg_proc.h"
19 #include "commands/seclabel.h"
20 #include "executor/executor.h"
22 #include "miscadmin.h"
24 #include "tcop/utility.h"
25 #include "utils/guc.h"
26 #include "utils/queryenvironment.h"
35 * Saved hook entries (if stacked)
37 static object_access_hook_type next_object_access_hook
= NULL
;
38 static ExecutorCheckPerms_hook_type next_exec_check_perms_hook
= NULL
;
39 static ProcessUtility_hook_type next_ProcessUtility_hook
= NULL
;
42 * Contextual information on DDL commands
49 * Name of the template database given by users on CREATE DATABASE
50 * command. Elsewhere (including the case of default) NULL.
52 const char *createdb_dtemplate
;
53 } sepgsql_context_info_t
;
55 static sepgsql_context_info_t sepgsql_context_info
;
58 * GUC: sepgsql.permissive = (on|off)
60 static bool sepgsql_permissive
= false;
63 sepgsql_get_permissive(void)
65 return sepgsql_permissive
;
69 * GUC: sepgsql.debug_audit = (on|off)
71 static bool sepgsql_debug_audit
= false;
74 sepgsql_get_debug_audit(void)
76 return sepgsql_debug_audit
;
80 * sepgsql_object_access
82 * Entrypoint of the object_access_hook. This routine performs as
83 * a dispatcher of invocation based on access type and object classes.
86 sepgsql_object_access(ObjectAccessType access
,
92 if (next_object_access_hook
)
93 (*next_object_access_hook
) (access
, classId
, objectId
, subId
, arg
);
99 ObjectAccessPostCreate
*pc_arg
= arg
;
102 is_internal
= pc_arg
? pc_arg
->is_internal
: false;
106 case DatabaseRelationId
:
107 Assert(!is_internal
);
108 sepgsql_database_post_create(objectId
,
109 sepgsql_context_info
.createdb_dtemplate
);
112 case NamespaceRelationId
:
113 Assert(!is_internal
);
114 sepgsql_schema_post_create(objectId
);
117 case RelationRelationId
:
121 * The cases in which we want to apply permission
122 * checks on creation of a new relation correspond
123 * to direct user invocation. For internal uses,
124 * that is creation of toast tables, index rebuild
125 * or ALTER TABLE commands, we need neither
126 * assignment of security labels nor permission
132 sepgsql_relation_post_create(objectId
);
135 sepgsql_attribute_post_create(objectId
, subId
);
138 case ProcedureRelationId
:
139 Assert(!is_internal
);
140 sepgsql_proc_post_create(objectId
);
144 /* Ignore unsupported object classes */
152 ObjectAccessDrop
*drop_arg
= (ObjectAccessDrop
*) arg
;
155 * No need to apply permission checks on object deletion due
156 * to internal cleanups; such as removal of temporary database
157 * object on session closed.
159 if ((drop_arg
->dropflags
& PERFORM_DELETION_INTERNAL
) != 0)
164 case DatabaseRelationId
:
165 sepgsql_database_drop(objectId
);
168 case NamespaceRelationId
:
169 sepgsql_schema_drop(objectId
);
172 case RelationRelationId
:
174 sepgsql_relation_drop(objectId
);
176 sepgsql_attribute_drop(objectId
, subId
);
179 case ProcedureRelationId
:
180 sepgsql_proc_drop(objectId
);
184 /* Ignore unsupported object classes */
194 case RelationRelationId
:
195 sepgsql_relation_truncate(objectId
);
198 /* Ignore unsupported object classes */
206 ObjectAccessPostAlter
*pa_arg
= arg
;
207 bool is_internal
= pa_arg
->is_internal
;
211 case DatabaseRelationId
:
212 Assert(!is_internal
);
213 sepgsql_database_setattr(objectId
);
216 case NamespaceRelationId
:
217 Assert(!is_internal
);
218 sepgsql_schema_setattr(objectId
);
221 case RelationRelationId
:
225 * A case when we don't want to apply permission
226 * check is that relation is internally altered
227 * without user's intention. E.g, no need to check
228 * on toast table/index to be renamed at end of
229 * the table rewrites.
234 sepgsql_relation_setattr(objectId
);
237 sepgsql_attribute_setattr(objectId
, subId
);
240 case ProcedureRelationId
:
241 Assert(!is_internal
);
242 sepgsql_proc_setattr(objectId
);
246 /* Ignore unsupported object classes */
252 case OAT_NAMESPACE_SEARCH
:
254 ObjectAccessNamespaceSearch
*ns_arg
= arg
;
257 * If stacked extension already decided not to allow users to
258 * search this schema, we just stick with that decision.
263 Assert(classId
== NamespaceRelationId
);
264 Assert(ns_arg
->result
);
266 = sepgsql_schema_search(objectId
,
267 ns_arg
->ereport_on_violation
);
271 case OAT_FUNCTION_EXECUTE
:
273 Assert(classId
== ProcedureRelationId
);
274 sepgsql_proc_execute(objectId
);
279 elog(ERROR
, "unexpected object access type: %d", (int) access
);
285 * sepgsql_exec_check_perms
287 * Entrypoint of DML permissions
290 sepgsql_exec_check_perms(List
*rangeTbls
, List
*rteperminfos
, bool abort
)
293 * If security provider is stacking and one of them replied 'false' at
294 * least, we don't need to check any more.
296 if (next_exec_check_perms_hook
&&
297 !(*next_exec_check_perms_hook
) (rangeTbls
, rteperminfos
, abort
))
300 if (!sepgsql_dml_privileges(rangeTbls
, rteperminfos
, abort
))
307 * sepgsql_utility_command
309 * It tries to rough-grained control on utility commands; some of them can
310 * break whole of the things if nefarious user would use.
313 sepgsql_utility_command(PlannedStmt
*pstmt
,
314 const char *queryString
,
316 ProcessUtilityContext context
,
317 ParamListInfo params
,
318 QueryEnvironment
*queryEnv
,
322 Node
*parsetree
= pstmt
->utilityStmt
;
323 sepgsql_context_info_t saved_context_info
= sepgsql_context_info
;
329 * Check command tag to avoid nefarious operations, and save the
330 * current contextual information to determine whether we should apply
331 * permission checks here, or not.
333 sepgsql_context_info
.cmdtype
= nodeTag(parsetree
);
335 switch (nodeTag(parsetree
))
340 * We hope to reference name of the source database, but it
341 * does not appear in system catalog. So, we save it here.
343 foreach(cell
, ((CreatedbStmt
*) parsetree
)->options
)
345 DefElem
*defel
= (DefElem
*) lfirst(cell
);
347 if (strcmp(defel
->defname
, "template") == 0)
349 sepgsql_context_info
.createdb_dtemplate
350 = strVal(defel
->arg
);
359 * We reject LOAD command across the board on enforcing mode,
360 * because a binary module can arbitrarily override hooks.
362 if (sepgsql_getenforce())
365 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
366 errmsg("SELinux: LOAD is not permitted")));
372 * Right now we don't check any other utility commands,
373 * because it needs more detailed information to make access
374 * control decision here, but we don't want to have two parse
375 * and analyze routines individually.
380 if (next_ProcessUtility_hook
)
381 (*next_ProcessUtility_hook
) (pstmt
, queryString
, readOnlyTree
,
382 context
, params
, queryEnv
,
385 standard_ProcessUtility(pstmt
, queryString
, readOnlyTree
,
386 context
, params
, queryEnv
,
391 sepgsql_context_info
= saved_context_info
;
397 * Module load/unload callback
403 * We allow to load the SE-PostgreSQL module on single-user-mode or
404 * shared_preload_libraries settings only.
406 if (IsUnderPostmaster
)
408 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
409 errmsg("sepgsql must be loaded via \"shared_preload_libraries\"")));
412 * Check availability of SELinux on the platform. If disabled, we cannot
413 * activate any SE-PostgreSQL features, and we have to skip rest of
416 if (is_selinux_enabled() < 1)
418 sepgsql_set_mode(SEPGSQL_MODE_DISABLED
);
423 * sepgsql.permissive = (on|off)
425 * This variable controls performing mode of SE-PostgreSQL on user's
428 DefineCustomBoolVariable("sepgsql.permissive",
429 "Turn on/off permissive mode in SE-PostgreSQL",
440 * sepgsql.debug_audit = (on|off)
442 * This variable allows users to turn on/off audit logs on access control
443 * decisions, independent from auditallow/auditdeny setting in the
444 * security policy. We intend to use this option for debugging purpose.
446 DefineCustomBoolVariable("sepgsql.debug_audit",
447 "Turn on/off debug audit messages",
449 &sepgsql_debug_audit
,
457 MarkGUCPrefixReserved("sepgsql");
459 /* Initialize userspace access vector cache */
462 /* Initialize security label of the client and related stuff */
463 sepgsql_init_client_label();
465 /* Security label provider hook */
466 register_label_provider(SEPGSQL_LABEL_TAG
,
467 sepgsql_object_relabel
);
469 /* Object access hook */
470 next_object_access_hook
= object_access_hook
;
471 object_access_hook
= sepgsql_object_access
;
473 /* DML permission check */
474 next_exec_check_perms_hook
= ExecutorCheckPerms_hook
;
475 ExecutorCheckPerms_hook
= sepgsql_exec_check_perms
;
477 /* ProcessUtility hook */
478 next_ProcessUtility_hook
= ProcessUtility_hook
;
479 ProcessUtility_hook
= sepgsql_utility_command
;
481 /* init contextual info */
482 memset(&sepgsql_context_info
, 0, sizeof(sepgsql_context_info
));