PostgreSQL Source Code  git master
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"
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"
27 
29 
30 /*
31  * Declarations
32  */
33 
34 /*
35  * Saved hook entries (if stacked)
36  */
40 
41 /*
42  * Contextual information on DDL commands
43  */
44 typedef 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  */
60 static bool sepgsql_permissive = false;
61 
62 bool
64 {
65  return sepgsql_permissive;
66 }
67 
68 /*
69  * GUC: sepgsql.debug_audit = (on|off)
70  */
71 static bool sepgsql_debug_audit = false;
72 
73 bool
75 {
76  return sepgsql_debug_audit;
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  */
85 static 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  {
99  ObjectAccessPostCreate *pc_arg = arg;
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);
114  sepgsql_schema_post_create(objectId);
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:
195  sepgsql_relation_truncate(objectId);
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  */
289 static bool
290 sepgsql_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  */
312 static 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  */
333  sepgsql_context_info.cmdtype = nodeTag(parsetree);
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  {
364  ereport(ERROR,
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/unload callback
398  */
399 void
400 _PG_init(void)
401 {
402  /*
403  * We allow to load the SE-PostgreSQL module on single-user-mode or
404  * shared_preload_libraries settings only.
405  */
406  if (IsUnderPostmaster)
407  ereport(ERROR,
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,
433  PGC_SIGHUP,
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,
451  PGC_USERSET,
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:858
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:85
bool sepgsql_dml_privileges(List *rangeTbls, List *rteperminfos, bool abort_on_violation)
Definition: dml.c:282
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define PG_TRY(...)
Definition: elog.h:370
#define PG_END_TRY(...)
Definition: elog.h:395
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define PG_FINALLY(...)
Definition: elog.h:387
#define ereport(elevel,...)
Definition: elog.h:149
ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook
Definition: execMain.c:71
bool(* ExecutorCheckPerms_hook_type)(List *rangeTable, List *rtePermInfos, bool ereport_on_violation)
Definition: executor.h:94
bool IsUnderPostmaster
Definition: globals.c:117
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:5075
void MarkGUCPrefixReserved(const char *className)
Definition: guc.c:5222
@ 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:1829
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:815
Node * arg
Definition: parsenodes.h:816
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:540
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