Revert commit 66c0185a3 and follow-on patches.
[pgsql.git] / contrib / sepgsql / hooks.c
blob0f206b1093d816a2038b6b5ee3ed40152a0b9cce
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 * -------------------------------------------------------------------------
11 #include "postgres.h"
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"
21 #include "fmgr.h"
22 #include "miscadmin.h"
23 #include "sepgsql.h"
24 #include "tcop/utility.h"
25 #include "utils/guc.h"
26 #include "utils/queryenvironment.h"
28 PG_MODULE_MAGIC;
31 * Declarations
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
44 typedef struct
46 NodeTag cmdtype;
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;
62 bool
63 sepgsql_get_permissive(void)
65 return sepgsql_permissive;
69 * GUC: sepgsql.debug_audit = (on|off)
71 static bool sepgsql_debug_audit = false;
73 bool
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.
85 static void
86 sepgsql_object_access(ObjectAccessType access,
87 Oid classId,
88 Oid objectId,
89 int subId,
90 void *arg)
92 if (next_object_access_hook)
93 (*next_object_access_hook) (access, classId, objectId, subId, arg);
95 switch (access)
97 case OAT_POST_CREATE:
99 ObjectAccessPostCreate *pc_arg = arg;
100 bool is_internal;
102 is_internal = pc_arg ? pc_arg->is_internal : false;
104 switch (classId)
106 case DatabaseRelationId:
107 Assert(!is_internal);
108 sepgsql_database_post_create(objectId,
109 sepgsql_context_info.createdb_dtemplate);
110 break;
112 case NamespaceRelationId:
113 Assert(!is_internal);
114 sepgsql_schema_post_create(objectId);
115 break;
117 case RelationRelationId:
118 if (subId == 0)
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
127 * checks.
129 if (is_internal)
130 break;
132 sepgsql_relation_post_create(objectId);
134 else
135 sepgsql_attribute_post_create(objectId, subId);
136 break;
138 case ProcedureRelationId:
139 Assert(!is_internal);
140 sepgsql_proc_post_create(objectId);
141 break;
143 default:
144 /* Ignore unsupported object classes */
145 break;
148 break;
150 case OAT_DROP:
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)
160 break;
162 switch (classId)
164 case DatabaseRelationId:
165 sepgsql_database_drop(objectId);
166 break;
168 case NamespaceRelationId:
169 sepgsql_schema_drop(objectId);
170 break;
172 case RelationRelationId:
173 if (subId == 0)
174 sepgsql_relation_drop(objectId);
175 else
176 sepgsql_attribute_drop(objectId, subId);
177 break;
179 case ProcedureRelationId:
180 sepgsql_proc_drop(objectId);
181 break;
183 default:
184 /* Ignore unsupported object classes */
185 break;
188 break;
190 case OAT_TRUNCATE:
192 switch (classId)
194 case RelationRelationId:
195 sepgsql_relation_truncate(objectId);
196 break;
197 default:
198 /* Ignore unsupported object classes */
199 break;
202 break;
204 case OAT_POST_ALTER:
206 ObjectAccessPostAlter *pa_arg = arg;
207 bool is_internal = pa_arg->is_internal;
209 switch (classId)
211 case DatabaseRelationId:
212 Assert(!is_internal);
213 sepgsql_database_setattr(objectId);
214 break;
216 case NamespaceRelationId:
217 Assert(!is_internal);
218 sepgsql_schema_setattr(objectId);
219 break;
221 case RelationRelationId:
222 if (subId == 0)
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.
231 if (is_internal)
232 break;
234 sepgsql_relation_setattr(objectId);
236 else
237 sepgsql_attribute_setattr(objectId, subId);
238 break;
240 case ProcedureRelationId:
241 Assert(!is_internal);
242 sepgsql_proc_setattr(objectId);
243 break;
245 default:
246 /* Ignore unsupported object classes */
247 break;
250 break;
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.
260 if (!ns_arg->result)
261 break;
263 Assert(classId == NamespaceRelationId);
264 Assert(ns_arg->result);
265 ns_arg->result
266 = sepgsql_schema_search(objectId,
267 ns_arg->ereport_on_violation);
269 break;
271 case OAT_FUNCTION_EXECUTE:
273 Assert(classId == ProcedureRelationId);
274 sepgsql_proc_execute(objectId);
276 break;
278 default:
279 elog(ERROR, "unexpected object access type: %d", (int) access);
280 break;
285 * sepgsql_exec_check_perms
287 * Entrypoint of DML permissions
289 static bool
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))
298 return false;
300 if (!sepgsql_dml_privileges(rangeTbls, rteperminfos, abort))
301 return false;
303 return true;
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.
312 static void
313 sepgsql_utility_command(PlannedStmt *pstmt,
314 const char *queryString,
315 bool readOnlyTree,
316 ProcessUtilityContext context,
317 ParamListInfo params,
318 QueryEnvironment *queryEnv,
319 DestReceiver *dest,
320 QueryCompletion *qc)
322 Node *parsetree = pstmt->utilityStmt;
323 sepgsql_context_info_t saved_context_info = sepgsql_context_info;
324 ListCell *cell;
326 PG_TRY();
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))
337 case T_CreatedbStmt:
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);
351 break;
354 break;
356 case T_LoadStmt:
359 * We reject LOAD command across the board on enforcing mode,
360 * because a binary module can arbitrarily override hooks.
362 if (sepgsql_getenforce())
364 ereport(ERROR,
365 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
366 errmsg("SELinux: LOAD is not permitted")));
368 break;
369 default:
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.
377 break;
380 if (next_ProcessUtility_hook)
381 (*next_ProcessUtility_hook) (pstmt, queryString, readOnlyTree,
382 context, params, queryEnv,
383 dest, qc);
384 else
385 standard_ProcessUtility(pstmt, queryString, readOnlyTree,
386 context, params, queryEnv,
387 dest, qc);
389 PG_FINALLY();
391 sepgsql_context_info = saved_context_info;
393 PG_END_TRY();
397 * Module load/unload callback
399 void
400 _PG_init(void)
403 * We allow to load the SE-PostgreSQL module on single-user-mode or
404 * shared_preload_libraries settings only.
406 if (IsUnderPostmaster)
407 ereport(ERROR,
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
414 * initialization.
416 if (is_selinux_enabled() < 1)
418 sepgsql_set_mode(SEPGSQL_MODE_DISABLED);
419 return;
423 * sepgsql.permissive = (on|off)
425 * This variable controls performing mode of SE-PostgreSQL on user's
426 * session.
428 DefineCustomBoolVariable("sepgsql.permissive",
429 "Turn on/off permissive mode in SE-PostgreSQL",
430 NULL,
431 &sepgsql_permissive,
432 false,
433 PGC_SIGHUP,
434 GUC_NOT_IN_SAMPLE,
435 NULL,
436 NULL,
437 NULL);
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",
448 NULL,
449 &sepgsql_debug_audit,
450 false,
451 PGC_USERSET,
452 GUC_NOT_IN_SAMPLE,
453 NULL,
454 NULL,
455 NULL);
457 MarkGUCPrefixReserved("sepgsql");
459 /* Initialize userspace access vector cache */
460 sepgsql_avc_init();
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));