]> granicus.if.org Git - postgresql/blob - contrib/sepgsql/hooks.c
Allow sepgsql labels to depend on object name.
[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-2013, 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
215                                                          * check on toast table/index to be renamed at
216                                                          * end of 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                 default:
240                         elog(ERROR, "unexpected object access type: %d", (int) access);
241                         break;
242         }
243 }
244
245 /*
246  * sepgsql_exec_check_perms
247  *
248  * Entrypoint of DML permissions
249  */
250 static bool
251 sepgsql_exec_check_perms(List *rangeTabls, bool abort)
252 {
253         /*
254          * If security provider is stacking and one of them replied 'false' at
255          * least, we don't need to check any more.
256          */
257         if (next_exec_check_perms_hook &&
258                 !(*next_exec_check_perms_hook) (rangeTabls, abort))
259                 return false;
260
261         if (!sepgsql_dml_privileges(rangeTabls, abort))
262                 return false;
263
264         return true;
265 }
266
267 /*
268  * sepgsql_utility_command
269  *
270  * It tries to rough-grained control on utility commands; some of them can
271  * break whole of the things if nefarious user would use.
272  */
273 static void
274 sepgsql_utility_command(Node *parsetree,
275                                                 const char *queryString,
276                                                 ParamListInfo params,
277                                                 DestReceiver *dest,
278                                                 char *completionTag,
279                                                 ProcessUtilityContext context)
280 {
281         sepgsql_context_info_t saved_context_info = sepgsql_context_info;
282         ListCell   *cell;
283
284         PG_TRY();
285         {
286                 /*
287                  * Check command tag to avoid nefarious operations, and save the
288                  * current contextual information to determine whether we should apply
289                  * permission checks here, or not.
290                  */
291                 sepgsql_context_info.cmdtype = nodeTag(parsetree);
292
293                 switch (nodeTag(parsetree))
294                 {
295                         case T_CreatedbStmt:
296
297                                 /*
298                                  * We hope to reference name of the source database, but it
299                                  * does not appear in system catalog. So, we save it here.
300                                  */
301                                 foreach(cell, ((CreatedbStmt *) parsetree)->options)
302                                 {
303                                         DefElem    *defel = (DefElem *) lfirst(cell);
304
305                                         if (strcmp(defel->defname, "template") == 0)
306                                         {
307                                                 sepgsql_context_info.createdb_dtemplate
308                                                         = strVal(defel->arg);
309                                                 break;
310                                         }
311                                 }
312                                 break;
313
314                         case T_LoadStmt:
315
316                                 /*
317                                  * We reject LOAD command across the board on enforcing mode,
318                                  * because a binary module can arbitrarily override hooks.
319                                  */
320                                 if (sepgsql_getenforce())
321                                 {
322                                         ereport(ERROR,
323                                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
324                                                          errmsg("SELinux: LOAD is not permitted")));
325                                 }
326                                 break;
327                         default:
328
329                                 /*
330                                  * Right now we don't check any other utility commands,
331                                  * because it needs more detailed information to make access
332                                  * control decision here, but we don't want to have two parse
333                                  * and analyze routines individually.
334                                  */
335                                 break;
336                 }
337
338                 if (next_ProcessUtility_hook)
339                         (*next_ProcessUtility_hook) (parsetree, queryString, params,
340                                                                                  dest, completionTag, context);
341                 else
342                         standard_ProcessUtility(parsetree, queryString, params,
343                                                                         dest, completionTag, context);
344         }
345         PG_CATCH();
346         {
347                 sepgsql_context_info = saved_context_info;
348                 PG_RE_THROW();
349         }
350         PG_END_TRY();
351         sepgsql_context_info = saved_context_info;
352 }
353
354 /*
355  * Module load/unload callback
356  */
357 void
358 _PG_init(void)
359 {
360         /*
361          * We allow to load the SE-PostgreSQL module on single-user-mode or
362          * shared_preload_libraries settings only.
363          */
364         if (IsUnderPostmaster)
365                 ereport(ERROR,
366                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
367                          errmsg("sepgsql must be loaded via shared_preload_libraries")));
368
369         /*
370          * Check availability of SELinux on the platform. If disabled, we cannot
371          * activate any SE-PostgreSQL features, and we have to skip rest of
372          * initialization.
373          */
374         if (is_selinux_enabled() < 1)
375         {
376                 sepgsql_set_mode(SEPGSQL_MODE_DISABLED);
377                 return;
378         }
379
380         /*
381          * sepgsql.permissive = (on|off)
382          *
383          * This variable controls performing mode of SE-PostgreSQL on user's
384          * session.
385          */
386         DefineCustomBoolVariable("sepgsql.permissive",
387                                                          "Turn on/off permissive mode in SE-PostgreSQL",
388                                                          NULL,
389                                                          &sepgsql_permissive,
390                                                          false,
391                                                          PGC_SIGHUP,
392                                                          GUC_NOT_IN_SAMPLE,
393                                                          NULL,
394                                                          NULL,
395                                                          NULL);
396
397         /*
398          * sepgsql.debug_audit = (on|off)
399          *
400          * This variable allows users to turn on/off audit logs on access control
401          * decisions, independent from auditallow/auditdeny setting in the
402          * security policy. We intend to use this option for debugging purpose.
403          */
404         DefineCustomBoolVariable("sepgsql.debug_audit",
405                                                          "Turn on/off debug audit messages",
406                                                          NULL,
407                                                          &sepgsql_debug_audit,
408                                                          false,
409                                                          PGC_USERSET,
410                                                          GUC_NOT_IN_SAMPLE,
411                                                          NULL,
412                                                          NULL,
413                                                          NULL);
414
415         /* Initialize userspace access vector cache */
416         sepgsql_avc_init();
417
418         /* Initialize security label of the client and related stuff */
419         sepgsql_init_client_label();
420
421         /* Security label provider hook */
422         register_label_provider(SEPGSQL_LABEL_TAG,
423                                                         sepgsql_object_relabel);
424
425         /* Object access hook */
426         next_object_access_hook = object_access_hook;
427         object_access_hook = sepgsql_object_access;
428
429         /* DML permission check */
430         next_exec_check_perms_hook = ExecutorCheckPerms_hook;
431         ExecutorCheckPerms_hook = sepgsql_exec_check_perms;
432
433         /* ProcessUtility hook */
434         next_ProcessUtility_hook = ProcessUtility_hook;
435         ProcessUtility_hook = sepgsql_utility_command;
436
437         /* init contextual info */
438         memset(&sepgsql_context_info, 0, sizeof(sepgsql_context_info));
439 }