1 /* -------------------------------------------------------------------------
2  *
3  * contrib/sepgsql/hooks.c
4  *
5  * Entrypoints of the hooks in PostgreSQL, and dispatches the callbacks.
6  *
7  * Copyright (c) 2010-2016, 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
sepgsql_get_permissive(void)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
sepgsql_get_debug_audit(void)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
sepgsql_object_access(ObjectAccessType access,Oid classId,Oid objectId,int subId,void * arg)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
sepgsql_exec_check_perms(List * rangeTabls,bool abort)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
sepgsql_utility_command(Node * parsetree,const char * queryString,ProcessUtilityContext context,ParamListInfo params,DestReceiver * dest,char * completionTag)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
_PG_init(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 }
468