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-2024, 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
30/*
31 * Declarations
32 */
33
34/*
35 * Saved hook entries (if stacked)
36 */
40
41/*
42 * Contextual information on DDL commands
43 */
44typedef struct
45{
47
48 /*
49 * Name of the template database given by users on CREATE DATABASE
50 * command. Elsewhere (including the case of default) NULL.
51 */
52 const char *createdb_dtemplate;
54
56
57/*
58 * GUC: sepgsql.permissive = (on|off)
59 */
60static bool sepgsql_permissive = false;
61
62bool
64{
65 return sepgsql_permissive;
66}
67
68/*
69 * GUC: sepgsql.debug_audit = (on|off)
70 */
71static bool sepgsql_debug_audit = false;
72
73bool
75{
77}
78
79/*
80 * sepgsql_object_access
81 *
82 * Entrypoint of the object_access_hook. This routine performs as
83 * a dispatcher of invocation based on access type and object classes.
84 */
85static void
87 Oid classId,
88 Oid objectId,
89 int subId,
90 void *arg)
91{
93 (*next_object_access_hook) (access, classId, objectId, subId, arg);
94
95 switch (access)
96 {
97 case OAT_POST_CREATE:
98 {
100 bool is_internal;
101
102 is_internal = pc_arg ? pc_arg->is_internal : false;
103
104 switch (classId)
105 {
106 case DatabaseRelationId:
107 Assert(!is_internal);
110 break;
111
112 case NamespaceRelationId:
113 Assert(!is_internal);
115 break;
116
117 case RelationRelationId:
118 if (subId == 0)
119 {
120 /*
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.
128 */
129 if (is_internal)
130 break;
131
133 }
134 else
135 sepgsql_attribute_post_create(objectId, subId);
136 break;
137
138 case ProcedureRelationId:
139 Assert(!is_internal);
140 sepgsql_proc_post_create(objectId);
141 break;
142
143 default:
144 /* Ignore unsupported object classes */
145 break;
146 }
147 }
148 break;
149
150 case OAT_DROP:
151 {
152 ObjectAccessDrop *drop_arg = (ObjectAccessDrop *) arg;
153
154 /*
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.
158 */
159 if ((drop_arg->dropflags & PERFORM_DELETION_INTERNAL) != 0)
160 break;
161
162 switch (classId)
163 {
164 case DatabaseRelationId:
165 sepgsql_database_drop(objectId);
166 break;
167
168 case NamespaceRelationId:
169 sepgsql_schema_drop(objectId);
170 break;
171
172 case RelationRelationId:
173 if (subId == 0)
174 sepgsql_relation_drop(objectId);
175 else
176 sepgsql_attribute_drop(objectId, subId);
177 break;
178
179 case ProcedureRelationId:
180 sepgsql_proc_drop(objectId);
181 break;
182
183 default:
184 /* Ignore unsupported object classes */
185 break;
186 }
187 }
188 break;
189
190 case OAT_TRUNCATE:
191 {
192 switch (classId)
193 {
194 case RelationRelationId:
196 break;
197 default:
198 /* Ignore unsupported object classes */
199 break;
200 }
201 }
202 break;
203
204 case OAT_POST_ALTER:
205 {
206 ObjectAccessPostAlter *pa_arg = arg;
207 bool is_internal = pa_arg->is_internal;
208
209 switch (classId)
210 {
211 case DatabaseRelationId:
212 Assert(!is_internal);
213 sepgsql_database_setattr(objectId);
214 break;
215
216 case NamespaceRelationId:
217 Assert(!is_internal);
218 sepgsql_schema_setattr(objectId);
219 break;
220
221 case RelationRelationId:
222 if (subId == 0)
223 {
224 /*
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.
230 */
231 if (is_internal)
232 break;
233
234 sepgsql_relation_setattr(objectId);
235 }
236 else
237 sepgsql_attribute_setattr(objectId, subId);
238 break;
239
240 case ProcedureRelationId:
241 Assert(!is_internal);
242 sepgsql_proc_setattr(objectId);
243 break;
244
245 default:
246 /* Ignore unsupported object classes */
247 break;
248 }
249 }
250 break;
251
253 {
255
256 /*
257 * If stacked extension already decided not to allow users to
258 * search this schema, we just stick with that decision.
259 */
260 if (!ns_arg->result)
261 break;
262
263 Assert(classId == NamespaceRelationId);
264 Assert(ns_arg->result);
265 ns_arg->result
266 = sepgsql_schema_search(objectId,
267 ns_arg->ereport_on_violation);
268 }
269 break;
270
272 {
273 Assert(classId == ProcedureRelationId);
274 sepgsql_proc_execute(objectId);
275 }
276 break;
277
278 default:
279 elog(ERROR, "unexpected object access type: %d", (int) access);
280 break;
281 }
282}
283
284/*
285 * sepgsql_exec_check_perms
286 *
287 * Entrypoint of DML permissions
288 */
289static bool
290sepgsql_exec_check_perms(List *rangeTbls, List *rteperminfos, bool abort)
291{
292 /*
293 * If security provider is stacking and one of them replied 'false' at
294 * least, we don't need to check any more.
295 */
297 !(*next_exec_check_perms_hook) (rangeTbls, rteperminfos, abort))
298 return false;
299
300 if (!sepgsql_dml_privileges(rangeTbls, rteperminfos, abort))
301 return false;
302
303 return true;
304}
305
306/*
307 * sepgsql_utility_command
308 *
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.
311 */
312static void
314 const char *queryString,
315 bool readOnlyTree,
317 ParamListInfo params,
318 QueryEnvironment *queryEnv,
320 QueryCompletion *qc)
321{
322 Node *parsetree = pstmt->utilityStmt;
323 sepgsql_context_info_t saved_context_info = sepgsql_context_info;
324 ListCell *cell;
325
326 PG_TRY();
327 {
328 /*
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.
332 */
334
335 switch (nodeTag(parsetree))
336 {
337 case T_CreatedbStmt:
338
339 /*
340 * We hope to reference name of the source database, but it
341 * does not appear in system catalog. So, we save it here.
342 */
343 foreach(cell, ((CreatedbStmt *) parsetree)->options)
344 {
345 DefElem *defel = (DefElem *) lfirst(cell);
346
347 if (strcmp(defel->defname, "template") == 0)
348 {
350 = strVal(defel->arg);
351 break;
352 }
353 }
354 break;
355
356 case T_LoadStmt:
357
358 /*
359 * We reject LOAD command across the board on enforcing mode,
360 * because a binary module can arbitrarily override hooks.
361 */
362 if (sepgsql_getenforce())
363 {
365 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
366 errmsg("SELinux: LOAD is not permitted")));
367 }
368 break;
369 default:
370
371 /*
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.
376 */
377 break;
378 }
379
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);
388 }
389 PG_FINALLY();
390 {
391 sepgsql_context_info = saved_context_info;
392 }
393 PG_END_TRY();
394}
395
396/*
397 * Module load callback
398 */
399void
401{
402 /*
403 * We allow to load the SE-PostgreSQL module on single-user-mode or
404 * shared_preload_libraries settings only.
405 */
408 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
409 errmsg("sepgsql must be loaded via \"shared_preload_libraries\"")));
410
411 /*
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.
415 */
416 if (is_selinux_enabled() < 1)
417 {
419 return;
420 }
421
422 /*
423 * sepgsql.permissive = (on|off)
424 *
425 * This variable controls performing mode of SE-PostgreSQL on user's
426 * session.
427 */
428 DefineCustomBoolVariable("sepgsql.permissive",
429 "Turn on/off permissive mode in SE-PostgreSQL",
430 NULL,
432 false,
435 NULL,
436 NULL,
437 NULL);
438
439 /*
440 * sepgsql.debug_audit = (on|off)
441 *
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.
445 */
446 DefineCustomBoolVariable("sepgsql.debug_audit",
447 "Turn on/off debug audit messages",
448 NULL,
450 false,
453 NULL,
454 NULL,
455 NULL);
456
457 MarkGUCPrefixReserved("sepgsql");
458
459 /* Initialize userspace access vector cache */
461
462 /* Initialize security label of the client and related stuff */
464
465 /* Security label provider hook */
468
469 /* Object access hook */
472
473 /* DML permission check */
476
477 /* ProcessUtility hook */
480
481 /* init contextual info */
482 memset(&sepgsql_context_info, 0, sizeof(sepgsql_context_info));
483}
#define Assert(condition)
Definition: c.h:812
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:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#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:73
bool(* ExecutorCheckPerms_hook_type)(List *rangeTable, List *rtePermInfos, bool ereport_on_violation)
Definition: executor.h:93
bool IsUnderPostmaster
Definition: globals.c:119
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:5132
void MarkGUCPrefixReserved(const char *className)
Definition: guc.c:5279
@ PGC_USERSET
Definition: guc.h:75
@ PGC_SIGHUP
Definition: guc.h:71
#define GUC_NOT_IN_SAMPLE
Definition: guc.h:217
static sepgsql_context_info_t sepgsql_context_info
Definition: hooks.c:55
bool sepgsql_get_permissive(void)
Definition: hooks.c:63
void _PG_init(void)
Definition: hooks.c:400
static ExecutorCheckPerms_hook_type next_exec_check_perms_hook
Definition: hooks.c:38
static ProcessUtility_hook_type next_ProcessUtility_hook
Definition: hooks.c:39
static void sepgsql_object_access(ObjectAccessType access, Oid classId, Oid objectId, int subId, void *arg)
Definition: hooks.c:86
static bool sepgsql_debug_audit
Definition: hooks.c:71
PG_MODULE_MAGIC
Definition: hooks.c:28
bool sepgsql_get_debug_audit(void)
Definition: hooks.c:74
static bool sepgsql_permissive
Definition: hooks.c:60
static bool sepgsql_exec_check_perms(List *rangeTbls, List *rteperminfos, bool abort)
Definition: hooks.c:290
static object_access_hook_type next_object_access_hook
Definition: hooks.c:37
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:313
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:133
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:31
short access
Definition: preproc-type.c:36
tree context
Definition: radixtree.h:1837
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:817
Node * arg
Definition: parsenodes.h:818
Definition: pg_list.h:54
Definition: nodes.h:129
Node * utilityStmt
Definition: plannodes.h:95
const char * createdb_dtemplate
Definition: hooks.c:52
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