Age Owner Branch data TLA Line data Source code
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 : :
4830 rhaas@postgresql.org 28 :UBC 0 : PG_MODULE_MAGIC;
29 : :
30 : : /*
31 : : * Declarations
32 : : */
33 : :
34 : : /*
35 : : * Saved hook entries (if stacked)
36 : : */
37 : : static object_access_hook_type next_object_access_hook = NULL;
38 : : static ExecutorCheckPerms_hook_type next_exec_check_perms_hook = NULL;
39 : : static ProcessUtility_hook_type next_ProcessUtility_hook = NULL;
40 : :
41 : : /*
42 : : * Contextual information on DDL commands
43 : : */
44 : : typedef struct
45 : : {
46 : : NodeTag cmdtype;
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;
53 : : } sepgsql_context_info_t;
54 : :
55 : : static sepgsql_context_info_t sepgsql_context_info;
56 : :
57 : : /*
58 : : * GUC: sepgsql.permissive = (on|off)
59 : : */
60 : : static bool sepgsql_permissive = false;
61 : :
62 : : bool
63 : 0 : sepgsql_get_permissive(void)
64 : : {
65 : 0 : return sepgsql_permissive;
66 : : }
67 : :
68 : : /*
69 : : * GUC: sepgsql.debug_audit = (on|off)
70 : : */
71 : : static bool sepgsql_debug_audit = false;
72 : :
73 : : bool
74 : 0 : sepgsql_get_debug_audit(void)
75 : : {
76 : 0 : 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
86 : 0 : sepgsql_object_access(ObjectAccessType access,
87 : : Oid classId,
88 : : Oid objectId,
89 : : int subId,
90 : : void *arg)
91 : : {
92 [ # # ]: 0 : if (next_object_access_hook)
4419 93 : 0 : (*next_object_access_hook) (access, classId, objectId, subId, arg);
94 : :
4830 95 [ # # # # : 0 : switch (access)
# # # ]
96 : : {
97 : 0 : case OAT_POST_CREATE:
98 : : {
4191 alvherre@alvh.no-ip. 99 : 0 : ObjectAccessPostCreate *pc_arg = arg;
100 : : bool is_internal;
101 : :
102 [ # # # # : 0 : is_internal = pc_arg ? pc_arg->is_internal : false;
# # # #
# ]
103 : :
104 : : switch (classId)
105 : : {
106 : 0 : case DatabaseRelationId:
107 [ # # ]: 0 : Assert(!is_internal);
108 : 0 : sepgsql_database_post_create(objectId,
109 : : sepgsql_context_info.createdb_dtemplate);
110 : 0 : break;
111 : :
112 : 0 : case NamespaceRelationId:
113 [ # # ]: 0 : Assert(!is_internal);
114 : 0 : sepgsql_schema_post_create(objectId);
115 : 0 : break;
116 : :
117 : 0 : case RelationRelationId:
118 [ # # ]: 0 : 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 [ # # ]: 0 : if (is_internal)
4498 rhaas@postgresql.org 130 : 0 : break;
131 : :
4191 alvherre@alvh.no-ip. 132 : 0 : sepgsql_relation_post_create(objectId);
133 : : }
134 : : else
135 : 0 : sepgsql_attribute_post_create(objectId, subId);
136 : 0 : break;
137 : :
138 : 0 : case ProcedureRelationId:
139 [ # # ]: 0 : Assert(!is_internal);
140 : 0 : sepgsql_proc_post_create(objectId);
141 : 0 : break;
142 : :
143 : 0 : default:
144 : : /* Ignore unsupported object classes */
145 : 0 : break;
146 : : }
147 : : }
4830 rhaas@postgresql.org 148 : 0 : break;
149 : :
4419 150 : 0 : case OAT_DROP:
151 : : {
4326 bruce@momjian.us 152 : 0 : 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 : : */
4419 rhaas@postgresql.org 159 [ # # ]: 0 : if ((drop_arg->dropflags & PERFORM_DELETION_INTERNAL) != 0)
160 : 0 : break;
161 : :
162 : : switch (classId)
163 : : {
164 : 0 : case DatabaseRelationId:
165 : 0 : sepgsql_database_drop(objectId);
166 : 0 : break;
167 : :
168 : 0 : case NamespaceRelationId:
169 : 0 : sepgsql_schema_drop(objectId);
170 : 0 : break;
171 : :
172 : 0 : case RelationRelationId:
173 [ # # ]: 0 : if (subId == 0)
174 : 0 : sepgsql_relation_drop(objectId);
175 : : else
176 : 0 : sepgsql_attribute_drop(objectId, subId);
177 : 0 : break;
178 : :
179 : 0 : case ProcedureRelationId:
180 : 0 : sepgsql_proc_drop(objectId);
181 : 0 : break;
182 : :
4036 183 : 0 : default:
184 : : /* Ignore unsupported object classes */
185 : 0 : break;
186 : : }
187 : : }
188 : 0 : break;
189 : :
1604 mail@joeconway.com 190 [ # # ]: 0 : case OAT_TRUNCATE:
191 : : {
192 : : switch (classId)
193 : : {
194 : 0 : case RelationRelationId:
195 : 0 : sepgsql_relation_truncate(objectId);
196 : 0 : break;
197 : 0 : default:
198 : : /* Ignore unsupported object classes */
199 : 0 : break;
200 : : }
201 : : }
202 : 0 : break;
203 : :
4036 rhaas@postgresql.org 204 : 0 : case OAT_POST_ALTER:
205 : : {
3973 bruce@momjian.us 206 : 0 : ObjectAccessPostAlter *pa_arg = arg;
207 [ # # # # : 0 : bool is_internal = pa_arg->is_internal;
# ]
208 : :
209 : : switch (classId)
210 : : {
4036 rhaas@postgresql.org 211 : 0 : case DatabaseRelationId:
212 [ # # ]: 0 : Assert(!is_internal);
213 : 0 : sepgsql_database_setattr(objectId);
214 : 0 : break;
215 : :
216 : 0 : case NamespaceRelationId:
217 [ # # ]: 0 : Assert(!is_internal);
218 : 0 : sepgsql_schema_setattr(objectId);
219 : 0 : break;
220 : :
221 : 0 : case RelationRelationId:
222 [ # # ]: 0 : 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 [ # # ]: 0 : if (is_internal)
3973 bruce@momjian.us 232 : 0 : break;
233 : :
4036 rhaas@postgresql.org 234 : 0 : sepgsql_relation_setattr(objectId);
235 : : }
236 : : else
3973 bruce@momjian.us 237 : 0 : sepgsql_attribute_setattr(objectId, subId);
4036 rhaas@postgresql.org 238 : 0 : break;
239 : :
240 : 0 : case ProcedureRelationId:
241 [ # # ]: 0 : Assert(!is_internal);
242 : 0 : sepgsql_proc_setattr(objectId);
243 : 0 : break;
244 : :
4419 245 : 0 : default:
246 : : /* Ignore unsupported object classes */
247 : 0 : break;
248 : : }
249 : : }
250 : 0 : break;
251 : :
4027 252 : 0 : case OAT_NAMESPACE_SEARCH:
253 : : {
3973 bruce@momjian.us 254 : 0 : ObjectAccessNamespaceSearch *ns_arg = arg;
255 : :
256 : : /*
257 : : * If stacked extension already decided not to allow users to
258 : : * search this schema, we just stick with that decision.
259 : : */
4027 rhaas@postgresql.org 260 [ # # ]: 0 : if (!ns_arg->result)
261 : 0 : break;
262 : :
263 [ # # ]: 0 : Assert(classId == NamespaceRelationId);
264 [ # # ]: 0 : Assert(ns_arg->result);
265 : : ns_arg->result
266 : 0 : = sepgsql_schema_search(objectId,
267 : 0 : ns_arg->ereport_on_violation);
268 : : }
269 : 0 : break;
270 : :
4020 271 : 0 : case OAT_FUNCTION_EXECUTE:
272 : : {
273 [ # # ]: 0 : Assert(classId == ProcedureRelationId);
274 : 0 : sepgsql_proc_execute(objectId);
275 : : }
276 : 0 : break;
277 : :
4830 278 : 0 : default:
4753 bruce@momjian.us 279 [ # # ]: 0 : elog(ERROR, "unexpected object access type: %d", (int) access);
280 : : break;
281 : : }
4830 rhaas@postgresql.org 282 : 0 : }
283 : :
284 : : /*
285 : : * sepgsql_exec_check_perms
286 : : *
287 : : * Entrypoint of DML permissions
288 : : */
289 : : static bool
495 alvherre@alvh.no-ip. 290 : 0 : 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 : : */
4830 rhaas@postgresql.org 296 [ # # ]: 0 : if (next_exec_check_perms_hook &&
495 alvherre@alvh.no-ip. 297 [ # # ]: 0 : !(*next_exec_check_perms_hook) (rangeTbls, rteperminfos, abort))
4830 rhaas@postgresql.org 298 : 0 : return false;
299 : :
495 alvherre@alvh.no-ip. 300 [ # # ]: 0 : if (!sepgsql_dml_privileges(rangeTbls, rteperminfos, abort))
4830 rhaas@postgresql.org 301 : 0 : return false;
302 : :
303 : 0 : 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
2647 tgl@sss.pgh.pa.us 313 : 0 : sepgsql_utility_command(PlannedStmt *pstmt,
314 : : const char *queryString,
315 : : bool readOnlyTree,
316 : : ProcessUtilityContext context,
317 : : ParamListInfo params,
318 : : QueryEnvironment *queryEnv,
319 : : DestReceiver *dest,
320 : : QueryCompletion *qc)
321 : : {
322 : 0 : Node *parsetree = pstmt->utilityStmt;
4326 bruce@momjian.us 323 : 0 : sepgsql_context_info_t saved_context_info = sepgsql_context_info;
324 : : ListCell *cell;
325 : :
4498 rhaas@postgresql.org 326 [ # # ]: 0 : 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 : 0 : sepgsql_context_info.cmdtype = nodeTag(parsetree);
334 : :
335 [ # # # ]: 0 : switch (nodeTag(parsetree))
336 : : {
337 : 0 : 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 : : */
4326 bruce@momjian.us 343 [ # # # # : 0 : foreach(cell, ((CreatedbStmt *) parsetree)->options)
# # ]
344 : : {
345 : 0 : DefElem *defel = (DefElem *) lfirst(cell);
346 : :
4498 rhaas@postgresql.org 347 [ # # ]: 0 : if (strcmp(defel->defname, "template") == 0)
348 : : {
349 : : sepgsql_context_info.createdb_dtemplate
350 : 0 : = strVal(defel->arg);
351 : 0 : break;
352 : : }
353 : : }
354 : 0 : break;
355 : :
356 : 0 : 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 [ # # ]: 0 : if (sepgsql_getenforce())
363 : : {
364 [ # # ]: 0 : ereport(ERROR,
365 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
366 : : errmsg("SELinux: LOAD is not permitted")));
367 : : }
368 : 0 : break;
369 : 0 : 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 : 0 : break;
378 : : }
379 : :
380 [ # # ]: 0 : if (next_ProcessUtility_hook)
1031 tgl@sss.pgh.pa.us 381 : 0 : (*next_ProcessUtility_hook) (pstmt, queryString, readOnlyTree,
382 : : context, params, queryEnv,
383 : : dest, qc);
384 : : else
385 : 0 : standard_ProcessUtility(pstmt, queryString, readOnlyTree,
386 : : context, params, queryEnv,
387 : : dest, qc);
388 : : }
1626 peter@eisentraut.org 389 : 0 : PG_FINALLY();
390 : : {
4498 rhaas@postgresql.org 391 : 0 : sepgsql_context_info = saved_context_info;
392 : : }
393 [ # # ]: 0 : PG_END_TRY();
4830 394 : 0 : }
395 : :
396 : : /*
397 : : * Module load/unload callback
398 : : */
399 : : void
400 : 0 : _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 [ # # ]: 0 : if (IsUnderPostmaster)
407 [ # # ]: 0 : 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 [ # # ]: 0 : if (is_selinux_enabled() < 1)
417 : : {
418 : 0 : sepgsql_set_mode(SEPGSQL_MODE_DISABLED);
419 : 0 : 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 : 0 : DefineCustomBoolVariable("sepgsql.permissive",
429 : : "Turn on/off permissive mode in SE-PostgreSQL",
430 : : NULL,
431 : : &sepgsql_permissive,
432 : : false,
433 : : PGC_SIGHUP,
434 : : GUC_NOT_IN_SAMPLE,
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 : 0 : DefineCustomBoolVariable("sepgsql.debug_audit",
447 : : "Turn on/off debug audit messages",
448 : : NULL,
449 : : &sepgsql_debug_audit,
450 : : false,
451 : : PGC_USERSET,
452 : : GUC_NOT_IN_SAMPLE,
453 : : NULL,
454 : : NULL,
455 : : NULL);
456 : :
783 tgl@sss.pgh.pa.us 457 : 0 : MarkGUCPrefixReserved("sepgsql");
458 : :
459 : : /* Initialize userspace access vector cache */
4609 rhaas@postgresql.org 460 : 0 : sepgsql_avc_init();
461 : :
462 : : /* Initialize security label of the client and related stuff */
4442 463 : 0 : sepgsql_init_client_label();
464 : :
465 : : /* Security label provider hook */
4830 466 : 0 : register_label_provider(SEPGSQL_LABEL_TAG,
467 : : sepgsql_object_relabel);
468 : :
469 : : /* Object access hook */
470 : 0 : next_object_access_hook = object_access_hook;
471 : 0 : object_access_hook = sepgsql_object_access;
472 : :
473 : : /* DML permission check */
474 : 0 : next_exec_check_perms_hook = ExecutorCheckPerms_hook;
475 : 0 : ExecutorCheckPerms_hook = sepgsql_exec_check_perms;
476 : :
477 : : /* ProcessUtility hook */
478 : 0 : next_ProcessUtility_hook = ProcessUtility_hook;
479 : 0 : ProcessUtility_hook = sepgsql_utility_command;
480 : :
481 : : /* init contextual info */
4498 482 : 0 : memset(&sepgsql_context_info, 0, sizeof(sepgsql_context_info));
483 : : }
|