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