PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
hooks.c
Go to the documentation of this file.
1/* -------------------------------------------------------------------------
2 *
3 * contrib/sepgsql/hooks.c
4 *
5 * Entrypoints of the hooks in PostgreSQL, and dispatches the callbacks.
6 *
7 * Copyright (c) 2010-2025, PostgreSQL Global Development Group
8 *
9 * -------------------------------------------------------------------------
10 */
11#include "postgres.h"
12
13#include "catalog/dependency.h"
15#include "catalog/pg_class.h"
16#include "catalog/pg_database.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"
27
29 .name = "sepgsql",
30 .version = PG_VERSION
31);
32
33/*
34 * Declarations
35 */
36
37/*
38 * Saved hook entries (if stacked)
39 */
43
44/*
45 * Contextual information on DDL commands
46 */
47typedef struct
48{
50
51 /*
52 * Name of the template database given by users on CREATE DATABASE
53 * command. Elsewhere (including the case of default) NULL.
54 */
55 const char *createdb_dtemplate;
57
59
60/*
61 * GUC: sepgsql.permissive = (on|off)
62 */
63static bool sepgsql_permissive = false;
64
65bool
67{
68 return sepgsql_permissive;
69}
70
71/*
72 * GUC: sepgsql.debug_audit = (on|off)
73 */
74static bool sepgsql_debug_audit = false;
75
76bool
78{
80}
81
82/*
83 * sepgsql_object_access
84 *
85 * Entrypoint of the object_access_hook. This routine performs as
86 * a dispatcher of invocation based on access type and object classes.
87 */
88static void
90 Oid classId,
91 Oid objectId,
92 int subId,
93 void *arg)
94{
96 (*next_object_access_hook) (access, classId, objectId, subId, arg);
97
98 switch (access)
99 {
100 case OAT_POST_CREATE:
101 {
102 ObjectAccessPostCreate *pc_arg = arg;
103 bool is_internal;
104
105 is_internal = pc_arg ? pc_arg->is_internal : false;
106
107 switch (classId)
108 {
109 case DatabaseRelationId:
110 Assert(!is_internal);
113 break;
114
115 case NamespaceRelationId:
116 Assert(!is_internal);
118 break;
119
120 case RelationRelationId:
121 if (subId == 0)
122 {
123 /*
124 * The cases in which we want to apply permission
125 * checks on creation of a new relation correspond
126 * to direct user invocation. For internal uses,
127 * that is creation of toast tables, index rebuild
128 * or ALTER TABLE commands, we need neither
129 * assignment of security labels nor permission
130 * checks.
131 */
132 if (is_internal)
133 break;
134
136 }
137 else
138 sepgsql_attribute_post_create(objectId, subId);
139 break;
140
141 case ProcedureRelationId:
142 Assert(!is_internal);
143 sepgsql_proc_post_create(objectId);
144 break;
145
146 default:
147 /* Ignore unsupported object classes */
148 break;
149 }
150 }
151 break;
152
153 case OAT_DROP:
154 {
155 ObjectAccessDrop *drop_arg = (ObjectAccessDrop *) arg;
156
157 /*
158 * No need to apply permission checks on object deletion due
159 * to internal cleanups; such as removal of temporary database
160 * object on session closed.
161 */
162 if ((drop_arg->dropflags & PERFORM_DELETION_INTERNAL) != 0)
163 break;
164
165 switch (classId)
166 {
167 case DatabaseRelationId:
168 sepgsql_database_drop(objectId);
169 break;
170
171 case NamespaceRelationId:
172 sepgsql_schema_drop(objectId);
173 break;
174
175 case RelationRelationId:
176 if (subId == 0)
177 sepgsql_relation_drop(objectId);
178 else
179 sepgsql_attribute_drop(objectId, subId);
180 break;
181
182 case ProcedureRelationId:
183 sepgsql_proc_drop(objectId);
184 break;
185
186 default:
187 /* Ignore unsupported object classes */
188 break;
189 }
190 }
191 break;
192
193 case OAT_TRUNCATE:
194 {
195 switch (classId)
196 {
197 case RelationRelationId:
199 break;
200 default:
201 /* Ignore unsupported object classes */
202 break;
203 }
204 }
205 break;
206
207 case OAT_POST_ALTER:
208 {
209 ObjectAccessPostAlter *pa_arg = arg;
210 bool is_internal = pa_arg->is_internal;
211
212 switch (classId)
213 {
214 case DatabaseRelationId:
215 Assert(!is_internal);
216 sepgsql_database_setattr(objectId);
217 break;
218
219 case NamespaceRelationId:
220 Assert(!is_internal);
221 sepgsql_schema_setattr(objectId);
222 break;
223
224 case RelationRelationId:
225 if (subId == 0)
226 {
227 /*
228 * A case when we don't want to apply permission
229 * check is that relation is internally altered
230 * without user's intention. E.g, no need to check
231 * on toast table/index to be renamed at end of
232 * the table rewrites.
233 */
234 if (is_internal)
235 break;
236
237 sepgsql_relation_setattr(objectId);
238 }
239 else
240 sepgsql_attribute_setattr(objectId, subId);
241 break;
242
243 case ProcedureRelationId:
244 Assert(!is_internal);
245 sepgsql_proc_setattr(objectId);
246 break;
247
248 default:
249 /* Ignore unsupported object classes */
250 break;
251 }
252 }
253 break;
254
256 {
258
259 /*
260 * If stacked extension already decided not to allow users to
261 * search this schema, we just stick with that decision.
262 */
263 if (!ns_arg->result)
264 break;
265
266 Assert(classId == NamespaceRelationId);
267 Assert(ns_arg->result);
268 ns_arg->result
269 = sepgsql_schema_search(objectId,
270 ns_arg->ereport_on_violation);
271 }
272 break;
273
275 {
276 Assert(classId == ProcedureRelationId);
277 sepgsql_proc_execute(objectId);
278 }
279 break;
280
281 default:
282 elog(ERROR, "unexpected object access type: %d", (int) access);
283 break;
284 }
285}
286
287/*
288 * sepgsql_exec_check_perms
289 *
290 * Entrypoint of DML permissions
291 */
292static bool
293sepgsql_exec_check_perms(List *rangeTbls, List *rteperminfos, bool abort)
294{
295 /*
296 * If security provider is stacking and one of them replied 'false' at
297 * least, we don't need to check any more.
298 */
300 !(*next_exec_check_perms_hook) (rangeTbls, rteperminfos, abort))
301 return false;
302
303 if (!sepgsql_dml_privileges(rangeTbls, rteperminfos, abort))
304 return false;
305
306 return true;
307}
308
309/*
310 * sepgsql_utility_command
311 *
312 * It tries to rough-grained control on utility commands; some of them can
313 * break whole of the things if nefarious user would use.
314 */
315static void
317 const char *queryString,
318 bool readOnlyTree,
319 ProcessUtilityContext context,
320 ParamListInfo params,
321 QueryEnvironment *queryEnv,
323 QueryCompletion *qc)
324{
325 Node *parsetree = pstmt->utilityStmt;
326 sepgsql_context_info_t saved_context_info = sepgsql_context_info;
327 ListCell *cell;
328
329 PG_TRY();
330 {
331 /*
332 * Check command tag to avoid nefarious operations, and save the
333 * current contextual information to determine whether we should apply
334 * permission checks here, or not.
335 */
337
338 switch (nodeTag(parsetree))
339 {
340 case T_CreatedbStmt:
341
342 /*
343 * We hope to reference name of the source database, but it
344 * does not appear in system catalog. So, we save it here.
345 */
346 foreach(cell, ((CreatedbStmt *) parsetree)->options)
347 {
348 DefElem *defel = (DefElem *) lfirst(cell);
349
350 if (strcmp(defel->defname, "template") == 0)
351 {
353 = strVal(defel->arg);
354 break;
355 }
356 }
357 break;
358
359 case T_LoadStmt:
360
361 /*
362 * We reject LOAD command across the board on enforcing mode,
363 * because a binary module can arbitrarily override hooks.
364 */
365 if (sepgsql_getenforce())
366 {
368 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
369 errmsg("SELinux: LOAD is not permitted")));
370 }
371 break;
372 default:
373
374 /*
375 * Right now we don't check any other utility commands,
376 * because it needs more detailed information to make access
377 * control decision here, but we don't want to have two parse
378 * and analyze routines individually.
379 */
380 break;
381 }
382
384 (*next_ProcessUtility_hook) (pstmt, queryString, readOnlyTree,
385 context, params, queryEnv,
386 dest, qc);
387 else
388 standard_ProcessUtility(pstmt, queryString, readOnlyTree,
389 context, params, queryEnv,
390 dest, qc);
391 }
392 PG_FINALLY();
393 {
394 sepgsql_context_info = saved_context_info;
395 }
396 PG_END_TRY();
397}
398
399/*
400 * Module load callback
401 */
402void
404{
405 /*
406 * We allow to load the SE-PostgreSQL module on single-user-mode or
407 * shared_preload_libraries settings only.
408 */
411 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
412 errmsg("sepgsql must be loaded via \"shared_preload_libraries\"")));
413
414 /*
415 * Check availability of SELinux on the platform. If disabled, we cannot
416 * activate any SE-PostgreSQL features, and we have to skip rest of
417 * initialization.
418 */
419 if (is_selinux_enabled() < 1)
420 {
422 return;
423 }
424
425 /*
426 * sepgsql.permissive = (on|off)
427 *
428 * This variable controls performing mode of SE-PostgreSQL on user's
429 * session.
430 */
431 DefineCustomBoolVariable("sepgsql.permissive",
432 "Turn on/off permissive mode in SE-PostgreSQL",
433 NULL,
435 false,
438 NULL,
439 NULL,
440 NULL);
441
442 /*
443 * sepgsql.debug_audit = (on|off)
444 *
445 * This variable allows users to turn on/off audit logs on access control
446 * decisions, independent from auditallow/auditdeny setting in the
447 * security policy. We intend to use this option for debugging purpose.
448 */
449 DefineCustomBoolVariable("sepgsql.debug_audit",
450 "Turn on/off debug audit messages",
451 NULL,
453 false,
456 NULL,
457 NULL,
458 NULL);
459
460 MarkGUCPrefixReserved("sepgsql");
461
462 /* Initialize userspace access vector cache */
464
465 /* Initialize security label of the client and related stuff */
467
468 /* Security label provider hook */
471
472 /* Object access hook */
475
476 /* DML permission check */
479
480 /* ProcessUtility hook */
483
484 /* init contextual info */
485 memset(&sepgsql_context_info, 0, sizeof(sepgsql_context_info));
486}
void sepgsql_proc_post_create(Oid functionId)
Definition: proc.c:37
void sepgsql_proc_setattr(Oid functionId)
Definition: proc.c:235
void sepgsql_proc_drop(Oid functionId)
Definition: proc.c:155
void sepgsql_proc_execute(Oid functionId)
Definition: proc.c:315
void sepgsql_attribute_setattr(Oid relOid, AttrNumber attnum)
Definition: relation.c:209
void sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum)
Definition: relation.c:43
void sepgsql_relation_post_create(Oid relOid)
Definition: relation.c:240
void sepgsql_relation_truncate(Oid relOid)
Definition: relation.c:524
void sepgsql_relation_setattr(Oid relOid)
Definition: relation.c:615
void sepgsql_relation_drop(Oid relOid)
Definition: relation.c:416
void sepgsql_attribute_drop(Oid relOid, AttrNumber attnum)
Definition: relation.c:133
void sepgsql_database_post_create(Oid databaseId, const char *dtemplate)
Definition: database.c:33
void sepgsql_database_drop(Oid databaseId)
Definition: database.c:133
void sepgsql_database_setattr(Oid databaseId)
Definition: database.c:160
#define PERFORM_DELETION_INTERNAL
Definition: dependency.h:92
bool sepgsql_dml_privileges(List *rangeTbls, List *rteperminfos, bool abort_on_violation)
Definition: dml.c:282
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define PG_TRY(...)
Definition: elog.h:371
#define PG_END_TRY(...)
Definition: elog.h:396
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define PG_FINALLY(...)
Definition: elog.h:388
#define ereport(elevel,...)
Definition: elog.h:149
ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook
Definition: execMain.c:76
bool(* ExecutorCheckPerms_hook_type)(List *rangeTable, List *rtePermInfos, bool ereport_on_violation)
Definition: executor.h:94
bool IsUnderPostmaster
Definition: globals.c:121
void DefineCustomBoolVariable(const char *name, const char *short_desc, const char *long_desc, bool *valueAddr, bool bootValue, GucContext context, int flags, GucBoolCheckHook check_hook, GucBoolAssignHook assign_hook, GucShowHook show_hook)
Definition: guc.c:5133
void MarkGUCPrefixReserved(const char *className)
Definition: guc.c:5280
@ PGC_USERSET
Definition: guc.h:79
@ PGC_SIGHUP
Definition: guc.h:75
#define GUC_NOT_IN_SAMPLE
Definition: guc.h:221
Assert(PointerIsAligned(start, uint64))
static sepgsql_context_info_t sepgsql_context_info
Definition: hooks.c:58
bool sepgsql_get_permissive(void)
Definition: hooks.c:66
void _PG_init(void)
Definition: hooks.c:403
static ExecutorCheckPerms_hook_type next_exec_check_perms_hook
Definition: hooks.c:41
PG_MODULE_MAGIC_EXT(.name="sepgsql",.version=PG_VERSION)
static ProcessUtility_hook_type next_ProcessUtility_hook
Definition: hooks.c:42
static void sepgsql_object_access(ObjectAccessType access, Oid classId, Oid objectId, int subId, void *arg)
Definition: hooks.c:89
static bool sepgsql_debug_audit
Definition: hooks.c:74
bool sepgsql_get_debug_audit(void)
Definition: hooks.c:77
static bool sepgsql_permissive
Definition: hooks.c:63
static bool sepgsql_exec_check_perms(List *rangeTbls, List *rteperminfos, bool abort)
Definition: hooks.c:293
static object_access_hook_type next_object_access_hook
Definition: hooks.c:40
static void sepgsql_utility_command(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition: hooks.c:316
void sepgsql_init_client_label(void)
Definition: label.c:404
void sepgsql_object_relabel(const ObjectAddress *object, const char *seclabel)
Definition: label.c:482
#define nodeTag(nodeptr)
Definition: nodes.h:139
NodeTag
Definition: nodes.h:27
object_access_hook_type object_access_hook
Definition: objectaccess.c:22
void(* object_access_hook_type)(ObjectAccessType access, Oid classId, Oid objectId, int subId, void *arg)
Definition: objectaccess.h:127
ObjectAccessType
Definition: objectaccess.h:49
@ OAT_NAMESPACE_SEARCH
Definition: objectaccess.h:53
@ OAT_FUNCTION_EXECUTE
Definition: objectaccess.h:54
@ OAT_DROP
Definition: objectaccess.h:51
@ OAT_TRUNCATE
Definition: objectaccess.h:55
@ OAT_POST_ALTER
Definition: objectaccess.h:52
@ OAT_POST_CREATE
Definition: objectaccess.h:50
void * arg
#define lfirst(lc)
Definition: pg_list.h:172
unsigned int Oid
Definition: postgres_ext.h:30
short access
Definition: preproc-type.c:36
void sepgsql_schema_post_create(Oid namespaceId)
Definition: schema.c:36
void sepgsql_schema_setattr(Oid namespaceId)
Definition: schema.c:202
bool sepgsql_schema_search(Oid namespaceId, bool abort_on_violation)
Definition: schema.c:209
void sepgsql_schema_drop(Oid namespaceId)
Definition: schema.c:114
void register_label_provider(const char *provider_name, check_object_relabel_type hook)
Definition: seclabel.c:570
int sepgsql_set_mode(int new_mode)
Definition: selinux.c:634
bool sepgsql_getenforce(void)
Definition: selinux.c:651
#define SEPGSQL_LABEL_TAG
Definition: sepgsql.h:23
#define SEPGSQL_MODE_DISABLED
Definition: sepgsql.h:31
void sepgsql_avc_init(void)
Definition: uavc.c:488
char * defname
Definition: parsenodes.h:826
Node * arg
Definition: parsenodes.h:827
Definition: pg_list.h:54
Definition: nodes.h:135
Node * utilityStmt
Definition: plannodes.h:139
const char * createdb_dtemplate
Definition: hooks.c:55
void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition: utility.c:543
ProcessUtility_hook_type ProcessUtility_hook
Definition: utility.c:70
void(* ProcessUtility_hook_type)(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition: utility.h:71
ProcessUtilityContext
Definition: utility.h:21
#define strVal(v)
Definition: value.h:82
const char * name