]> granicus.if.org Git - postgresql/blob - contrib/sepgsql/hooks.c
Fix initialization of fake LSN for unlogged relations
[postgresql] / contrib / sepgsql / hooks.c
1 /* -------------------------------------------------------------------------
2  *
3  * contrib/sepgsql/hooks.c
4  *
5  * Entrypoints of the hooks in PostgreSQL, and dispatches the callbacks.
6  *
7  * Copyright (c) 2010-2019, 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
28 PG_MODULE_MAGIC;
29
30 /*
31  * Declarations
32  */
33 void            _PG_init(void);
34
35 /*
36  * Saved hook entries (if stacked)
37  */
38 static object_access_hook_type next_object_access_hook = NULL;
39 static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL;
40 static ProcessUtility_hook_type next_ProcessUtility_hook = NULL;
41
42 /*
43  * Contextual information on DDL commands
44  */
45 typedef struct
46 {
47         NodeTag         cmdtype;
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;
54 }                       sepgsql_context_info_t;
55
56 static sepgsql_context_info_t sepgsql_context_info;
57
58 /*
59  * GUC: sepgsql.permissive = (on|off)
60  */
61 static bool sepgsql_permissive;
62
63 bool
64 sepgsql_get_permissive(void)
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
75 sepgsql_get_debug_audit(void)
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
87 sepgsql_object_access(ObjectAccessType access,
88                                           Oid classId,
89                                           Oid objectId,
90                                           int subId,
91                                           void *arg)
92 {
93         if (next_object_access_hook)
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);
109                                                 sepgsql_database_post_create(objectId,
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
133                                                         sepgsql_relation_post_create(objectId);
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
239                 case OAT_NAMESPACE_SEARCH:
240                         {
241                                 ObjectAccessNamespaceSearch *ns_arg = arg;
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
258                 case OAT_FUNCTION_EXECUTE:
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          */
283         if (next_exec_check_perms_hook &&
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
300 sepgsql_utility_command(PlannedStmt *pstmt,
301                                                 const char *queryString,
302                                                 ProcessUtilityContext context,
303                                                 ParamListInfo params,
304                                                 QueryEnvironment *queryEnv,
305                                                 DestReceiver *dest,
306                                                 char *completionTag)
307 {
308         Node       *parsetree = pstmt->utilityStmt;
309         sepgsql_context_info_t saved_context_info = sepgsql_context_info;
310         ListCell   *cell;
311
312         PG_TRY();
313         {
314                 /*
315                  * Check command tag to avoid nefarious operations, and save the
316                  * current contextual information to determine whether we should apply
317                  * permission checks here, or not.
318                  */
319                 sepgsql_context_info.cmdtype = nodeTag(parsetree);
320
321                 switch (nodeTag(parsetree))
322                 {
323                         case T_CreatedbStmt:
324
325                                 /*
326                                  * We hope to reference name of the source database, but it
327                                  * does not appear in system catalog. So, we save it here.
328                                  */
329                                 foreach(cell, ((CreatedbStmt *) parsetree)->options)
330                                 {
331                                         DefElem    *defel = (DefElem *) lfirst(cell);
332
333                                         if (strcmp(defel->defname, "template") == 0)
334                                         {
335                                                 sepgsql_context_info.createdb_dtemplate
336                                                         = strVal(defel->arg);
337                                                 break;
338                                         }
339                                 }
340                                 break;
341
342                         case T_LoadStmt:
343
344                                 /*
345                                  * We reject LOAD command across the board on enforcing mode,
346                                  * because a binary module can arbitrarily override hooks.
347                                  */
348                                 if (sepgsql_getenforce())
349                                 {
350                                         ereport(ERROR,
351                                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
352                                                          errmsg("SELinux: LOAD is not permitted")));
353                                 }
354                                 break;
355                         default:
356
357                                 /*
358                                  * Right now we don't check any other utility commands,
359                                  * because it needs more detailed information to make access
360                                  * control decision here, but we don't want to have two parse
361                                  * and analyze routines individually.
362                                  */
363                                 break;
364                 }
365
366                 if (next_ProcessUtility_hook)
367                         (*next_ProcessUtility_hook) (pstmt, queryString,
368                                                                                  context, params, queryEnv,
369                                                                                  dest, completionTag);
370                 else
371                         standard_ProcessUtility(pstmt, queryString,
372                                                                         context, params, queryEnv,
373                                                                         dest, completionTag);
374         }
375         PG_CATCH();
376         {
377                 sepgsql_context_info = saved_context_info;
378                 PG_RE_THROW();
379         }
380         PG_END_TRY();
381         sepgsql_context_info = saved_context_info;
382 }
383
384 /*
385  * Module load/unload callback
386  */
387 void
388 _PG_init(void)
389 {
390         /*
391          * We allow to load the SE-PostgreSQL module on single-user-mode or
392          * shared_preload_libraries settings only.
393          */
394         if (IsUnderPostmaster)
395                 ereport(ERROR,
396                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
397                                  errmsg("sepgsql must be loaded via shared_preload_libraries")));
398
399         /*
400          * Check availability of SELinux on the platform. If disabled, we cannot
401          * activate any SE-PostgreSQL features, and we have to skip rest of
402          * initialization.
403          */
404         if (is_selinux_enabled() < 1)
405         {
406                 sepgsql_set_mode(SEPGSQL_MODE_DISABLED);
407                 return;
408         }
409
410         /*
411          * sepgsql.permissive = (on|off)
412          *
413          * This variable controls performing mode of SE-PostgreSQL on user's
414          * session.
415          */
416         DefineCustomBoolVariable("sepgsql.permissive",
417                                                          "Turn on/off permissive mode in SE-PostgreSQL",
418                                                          NULL,
419                                                          &sepgsql_permissive,
420                                                          false,
421                                                          PGC_SIGHUP,
422                                                          GUC_NOT_IN_SAMPLE,
423                                                          NULL,
424                                                          NULL,
425                                                          NULL);
426
427         /*
428          * sepgsql.debug_audit = (on|off)
429          *
430          * This variable allows users to turn on/off audit logs on access control
431          * decisions, independent from auditallow/auditdeny setting in the
432          * security policy. We intend to use this option for debugging purpose.
433          */
434         DefineCustomBoolVariable("sepgsql.debug_audit",
435                                                          "Turn on/off debug audit messages",
436                                                          NULL,
437                                                          &sepgsql_debug_audit,
438                                                          false,
439                                                          PGC_USERSET,
440                                                          GUC_NOT_IN_SAMPLE,
441                                                          NULL,
442                                                          NULL,
443                                                          NULL);
444
445         /* Initialize userspace access vector cache */
446         sepgsql_avc_init();
447
448         /* Initialize security label of the client and related stuff */
449         sepgsql_init_client_label();
450
451         /* Security label provider hook */
452         register_label_provider(SEPGSQL_LABEL_TAG,
453                                                         sepgsql_object_relabel);
454
455         /* Object access hook */
456         next_object_access_hook = object_access_hook;
457         object_access_hook = sepgsql_object_access;
458
459         /* DML permission check */
460         next_exec_check_perms_hook = ExecutorCheckPerms_hook;
461         ExecutorCheckPerms_hook = sepgsql_exec_check_perms;
462
463         /* ProcessUtility hook */
464         next_ProcessUtility_hook = ProcessUtility_hook;
465         ProcessUtility_hook = sepgsql_utility_command;
466
467         /* init contextual info */
468         memset(&sepgsql_context_info, 0, sizeof(sepgsql_context_info));
469 }