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