1 /* -------------------------------------------------------------------------
2  *
3  * contrib/sepgsql/label.c
4  *
5  * Routines to support SELinux labels (security context)
6  *
7  * Copyright (c) 2010-2016, PostgreSQL Global Development Group
8  *
9  * -------------------------------------------------------------------------
10  */
11 #include "postgres.h"
12 
13 #include <selinux/label.h>
14 
15 /*
16  * <selinux/label.h> includes <stdbool.h>, which creates an incompatible
17  * #define for bool.  Get rid of that so we can use our own typedef.
18  * (We don't care if <stdbool.h> redefines "true"/"false"; those are close
19  * enough.)
20  */
21 #undef bool
22 
23 #include "access/heapam.h"
24 #include "access/htup_details.h"
25 #include "access/genam.h"
26 #include "access/xact.h"
27 #include "catalog/catalog.h"
28 #include "catalog/dependency.h"
29 #include "catalog/indexing.h"
30 #include "catalog/pg_attribute.h"
31 #include "catalog/pg_class.h"
32 #include "catalog/pg_database.h"
33 #include "catalog/pg_namespace.h"
34 #include "catalog/pg_proc.h"
35 #include "commands/dbcommands.h"
36 #include "commands/seclabel.h"
37 #include "libpq/auth.h"
38 #include "libpq/libpq-be.h"
39 #include "miscadmin.h"
40 #include "utils/builtins.h"
41 #include "utils/fmgroids.h"
42 #include "utils/guc.h"
43 #include "utils/lsyscache.h"
44 #include "utils/memutils.h"
45 #include "utils/rel.h"
46 #include "utils/tqual.h"
47 
48 #include "sepgsql.h"
49 
50 /*
51  * Saved hook entries (if stacked)
52  */
53 static ClientAuthentication_hook_type next_client_auth_hook = NULL;
54 static needs_fmgr_hook_type next_needs_fmgr_hook = NULL;
55 static fmgr_hook_type next_fmgr_hook = NULL;
56 
57 /*
58  * client_label_*
59  *
60  * security label of the database client.  Initially the client security label
61  * is equal to client_label_peer, and can be changed by one or more calls to
62  * sepgsql_setcon(), and also be temporarily overridden during execution of a
63  * trusted-procedure.
64  *
65  * sepgsql_setcon() is a transaction-aware operation; a (sub-)transaction
66  * rollback should also rollback the current client security label.  Therefore
67  * we use the list client_label_pending of pending_label to keep track of which
68  * labels were set during the (sub-)transactions.
69  */
70 static char *client_label_peer = NULL;	/* set by getpeercon(3) */
71 static List *client_label_pending = NIL;		/* pending list being set by
72 												 * sepgsql_setcon() */
73 static char *client_label_committed = NULL;		/* set by sepgsql_setcon(),
74 												 * and already committed */
75 static char *client_label_func = NULL;	/* set by trusted procedure */
76 
77 typedef struct
78 {
79 	SubTransactionId subid;
80 	char	   *label;
81 }	pending_label;
82 
83 /*
84  * sepgsql_get_client_label
85  *
86  * Returns the current security label of the client.  All code should use this
87  * routine to get the current label, instead of referring to the client_label_*
88  * variables above.
89  */
90 char *
sepgsql_get_client_label(void)91 sepgsql_get_client_label(void)
92 {
93 	/* trusted procedure client label override */
94 	if (client_label_func)
95 		return client_label_func;
96 
97 	/* uncommitted sepgsql_setcon() value */
98 	if (client_label_pending)
99 	{
100 		pending_label *plabel = llast(client_label_pending);
101 
102 		if (plabel->label)
103 			return plabel->label;
104 	}
105 	else if (client_label_committed)
106 		return client_label_committed;	/* set by sepgsql_setcon() committed */
107 
108 	/* default label */
109 	Assert(client_label_peer != NULL);
110 	return client_label_peer;
111 }
112 
113 /*
114  * sepgsql_set_client_label
115  *
116  * This routine tries to switch the current security label of the client, and
117  * checks related permissions.  The supplied new label shall be added to the
118  * client_label_pending list, then saved at transaction-commit time to ensure
119  * transaction-awareness.
120  */
121 static void
sepgsql_set_client_label(const char * new_label)122 sepgsql_set_client_label(const char *new_label)
123 {
124 	const char *tcontext;
125 	MemoryContext oldcxt;
126 	pending_label *plabel;
127 
128 	/* Reset to the initial client label, if NULL */
129 	if (!new_label)
130 		tcontext = client_label_peer;
131 	else
132 	{
133 		if (security_check_context_raw((security_context_t) new_label) < 0)
134 			ereport(ERROR,
135 					(errcode(ERRCODE_INVALID_NAME),
136 					 errmsg("SELinux: invalid security label: \"%s\"",
137 							new_label)));
138 		tcontext = new_label;
139 	}
140 
141 	/* Check process:{setcurrent} permission. */
142 	sepgsql_avc_check_perms_label(sepgsql_get_client_label(),
143 								  SEPG_CLASS_PROCESS,
144 								  SEPG_PROCESS__SETCURRENT,
145 								  NULL,
146 								  true);
147 	/* Check process:{dyntransition} permission. */
148 	sepgsql_avc_check_perms_label(tcontext,
149 								  SEPG_CLASS_PROCESS,
150 								  SEPG_PROCESS__DYNTRANSITION,
151 								  NULL,
152 								  true);
153 
154 	/*
155 	 * Append the supplied new_label on the pending list until the current
156 	 * transaction is committed.
157 	 */
158 	oldcxt = MemoryContextSwitchTo(CurTransactionContext);
159 
160 	plabel = palloc0(sizeof(pending_label));
161 	plabel->subid = GetCurrentSubTransactionId();
162 	if (new_label)
163 		plabel->label = pstrdup(new_label);
164 	client_label_pending = lappend(client_label_pending, plabel);
165 
166 	MemoryContextSwitchTo(oldcxt);
167 }
168 
169 /*
170  * sepgsql_xact_callback
171  *
172  * A callback routine of transaction commit/abort/prepare.  Commit or abort
173  * changes in the client_label_pending list.
174  */
175 static void
sepgsql_xact_callback(XactEvent event,void * arg)176 sepgsql_xact_callback(XactEvent event, void *arg)
177 {
178 	if (event == XACT_EVENT_COMMIT)
179 	{
180 		if (client_label_pending != NIL)
181 		{
182 			pending_label *plabel = llast(client_label_pending);
183 			char	   *new_label;
184 
185 			if (plabel->label)
186 				new_label = MemoryContextStrdup(TopMemoryContext,
187 												plabel->label);
188 			else
189 				new_label = NULL;
190 
191 			if (client_label_committed)
192 				pfree(client_label_committed);
193 
194 			client_label_committed = new_label;
195 
196 			/*
197 			 * XXX - Note that items of client_label_pending are allocated on
198 			 * CurTransactionContext, thus, all acquired memory region shall
199 			 * be released implicitly.
200 			 */
201 			client_label_pending = NIL;
202 		}
203 	}
204 	else if (event == XACT_EVENT_ABORT)
205 		client_label_pending = NIL;
206 }
207 
208 /*
209  * sepgsql_subxact_callback
210  *
211  * A callback routine of sub-transaction start/abort/commit.  Releases all
212  * security labels that are set within the sub-transaction that is aborted.
213  */
214 static void
sepgsql_subxact_callback(SubXactEvent event,SubTransactionId mySubid,SubTransactionId parentSubid,void * arg)215 sepgsql_subxact_callback(SubXactEvent event, SubTransactionId mySubid,
216 						 SubTransactionId parentSubid, void *arg)
217 {
218 	ListCell   *cell;
219 	ListCell   *prev;
220 	ListCell   *next;
221 
222 	if (event == SUBXACT_EVENT_ABORT_SUB)
223 	{
224 		prev = NULL;
225 		for (cell = list_head(client_label_pending); cell; cell = next)
226 		{
227 			pending_label *plabel = lfirst(cell);
228 
229 			next = lnext(cell);
230 
231 			if (plabel->subid == mySubid)
232 				client_label_pending
233 					= list_delete_cell(client_label_pending, cell, prev);
234 			else
235 				prev = cell;
236 		}
237 	}
238 }
239 
240 /*
241  * sepgsql_client_auth
242  *
243  * Entrypoint of the client authentication hook.
244  * It switches the client label according to getpeercon(), and the current
245  * performing mode according to the GUC setting.
246  */
247 static void
sepgsql_client_auth(Port * port,int status)248 sepgsql_client_auth(Port *port, int status)
249 {
250 	if (next_client_auth_hook)
251 		(*next_client_auth_hook) (port, status);
252 
253 	/*
254 	 * In the case when authentication failed, the supplied socket shall be
255 	 * closed soon, so we don't need to do anything here.
256 	 */
257 	if (status != STATUS_OK)
258 		return;
259 
260 	/*
261 	 * Getting security label of the peer process using API of libselinux.
262 	 */
263 	if (getpeercon_raw(port->sock, &client_label_peer) < 0)
264 		ereport(FATAL,
265 				(errcode(ERRCODE_INTERNAL_ERROR),
266 				 errmsg("SELinux: unable to get peer label: %m")));
267 
268 	/*
269 	 * Switch the current performing mode from INTERNAL to either DEFAULT or
270 	 * PERMISSIVE.
271 	 */
272 	if (sepgsql_get_permissive())
273 		sepgsql_set_mode(SEPGSQL_MODE_PERMISSIVE);
274 	else
275 		sepgsql_set_mode(SEPGSQL_MODE_DEFAULT);
276 }
277 
278 /*
279  * sepgsql_needs_fmgr_hook
280  *
281  * It informs the core whether the supplied function is trusted procedure,
282  * or not. If true, sepgsql_fmgr_hook shall be invoked at start, end, and
283  * abort time of function invocation.
284  */
285 static bool
sepgsql_needs_fmgr_hook(Oid functionId)286 sepgsql_needs_fmgr_hook(Oid functionId)
287 {
288 	ObjectAddress object;
289 
290 	if (next_needs_fmgr_hook &&
291 		(*next_needs_fmgr_hook) (functionId))
292 		return true;
293 
294 	/*
295 	 * SELinux needs the function to be called via security_definer wrapper,
296 	 * if this invocation will take a domain-transition. We call these
297 	 * functions as trusted-procedure, if the security policy has a rule that
298 	 * switches security label of the client on execution.
299 	 */
300 	if (sepgsql_avc_trusted_proc(functionId) != NULL)
301 		return true;
302 
303 	/*
304 	 * Even if not a trusted-procedure, this function should not be inlined
305 	 * unless the client has db_procedure:{execute} permission. Please note
306 	 * that it shall be actually failed later because of same reason with
307 	 * ACL_EXECUTE.
308 	 */
309 	object.classId = ProcedureRelationId;
310 	object.objectId = functionId;
311 	object.objectSubId = 0;
312 	if (!sepgsql_avc_check_perms(&object,
313 								 SEPG_CLASS_DB_PROCEDURE,
314 								 SEPG_DB_PROCEDURE__EXECUTE |
315 								 SEPG_DB_PROCEDURE__ENTRYPOINT,
316 								 SEPGSQL_AVC_NOAUDIT, false))
317 		return true;
318 
319 	return false;
320 }
321 
322 /*
323  * sepgsql_fmgr_hook
324  *
325  * It switches security label of the client on execution of trusted
326  * procedures.
327  */
328 static void
sepgsql_fmgr_hook(FmgrHookEventType event,FmgrInfo * flinfo,Datum * private)329 sepgsql_fmgr_hook(FmgrHookEventType event,
330 				  FmgrInfo *flinfo, Datum *private)
331 {
332 	struct
333 	{
334 		char	   *old_label;
335 		char	   *new_label;
336 		Datum		next_private;
337 	}		   *stack;
338 
339 	switch (event)
340 	{
341 		case FHET_START:
342 			stack = (void *) DatumGetPointer(*private);
343 			if (!stack)
344 			{
345 				MemoryContext oldcxt;
346 
347 				oldcxt = MemoryContextSwitchTo(flinfo->fn_mcxt);
348 				stack = palloc(sizeof(*stack));
349 				stack->old_label = NULL;
350 				stack->new_label = sepgsql_avc_trusted_proc(flinfo->fn_oid);
351 				stack->next_private = 0;
352 
353 				MemoryContextSwitchTo(oldcxt);
354 
355 				/*
356 				 * process:transition permission between old and new label,
357 				 * when user tries to switch security label of the client on
358 				 * execution of trusted procedure.
359 				 *
360 				 * Also, db_procedure:entrypoint permission should be checked
361 				 * whether this procedure can perform as an entrypoint of the
362 				 * trusted procedure, or not. Note that db_procedure:execute
363 				 * permission shall be checked individually.
364 				 */
365 				if (stack->new_label)
366 				{
367 					ObjectAddress object;
368 
369 					object.classId = ProcedureRelationId;
370 					object.objectId = flinfo->fn_oid;
371 					object.objectSubId = 0;
372 					sepgsql_avc_check_perms(&object,
373 											SEPG_CLASS_DB_PROCEDURE,
374 											SEPG_DB_PROCEDURE__ENTRYPOINT,
375 											getObjectDescription(&object),
376 											true);
377 
378 					sepgsql_avc_check_perms_label(stack->new_label,
379 												  SEPG_CLASS_PROCESS,
380 												  SEPG_PROCESS__TRANSITION,
381 												  NULL, true);
382 				}
383 				*private = PointerGetDatum(stack);
384 			}
385 			Assert(!stack->old_label);
386 			if (stack->new_label)
387 			{
388 				stack->old_label = client_label_func;
389 				client_label_func = stack->new_label;
390 			}
391 			if (next_fmgr_hook)
392 				(*next_fmgr_hook) (event, flinfo, &stack->next_private);
393 			break;
394 
395 		case FHET_END:
396 		case FHET_ABORT:
397 			stack = (void *) DatumGetPointer(*private);
398 
399 			if (next_fmgr_hook)
400 				(*next_fmgr_hook) (event, flinfo, &stack->next_private);
401 
402 			if (stack->new_label)
403 			{
404 				client_label_func = stack->old_label;
405 				stack->old_label = NULL;
406 			}
407 			break;
408 
409 		default:
410 			elog(ERROR, "unexpected event type: %d", (int) event);
411 			break;
412 	}
413 }
414 
415 /*
416  * sepgsql_init_client_label
417  *
418  * Initializes the client security label and sets up related hooks for client
419  * label management.
420  */
421 void
sepgsql_init_client_label(void)422 sepgsql_init_client_label(void)
423 {
424 	/*
425 	 * Set up dummy client label.
426 	 *
427 	 * XXX - note that PostgreSQL launches background worker process like
428 	 * autovacuum without authentication steps. So, we initialize sepgsql_mode
429 	 * with SEPGSQL_MODE_INTERNAL, and client_label with the security context
430 	 * of server process. Later, it also launches background of user session.
431 	 * In this case, the process is always hooked on post-authentication, and
432 	 * we can initialize the sepgsql_mode and client_label correctly.
433 	 */
434 	if (getcon_raw(&client_label_peer) < 0)
435 		ereport(ERROR,
436 				(errcode(ERRCODE_INTERNAL_ERROR),
437 				 errmsg("SELinux: failed to get server security label: %m")));
438 
439 	/* Client authentication hook */
440 	next_client_auth_hook = ClientAuthentication_hook;
441 	ClientAuthentication_hook = sepgsql_client_auth;
442 
443 	/* Trusted procedure hooks */
444 	next_needs_fmgr_hook = needs_fmgr_hook;
445 	needs_fmgr_hook = sepgsql_needs_fmgr_hook;
446 
447 	next_fmgr_hook = fmgr_hook;
448 	fmgr_hook = sepgsql_fmgr_hook;
449 
450 	/* Transaction/Sub-transaction callbacks */
451 	RegisterXactCallback(sepgsql_xact_callback, NULL);
452 	RegisterSubXactCallback(sepgsql_subxact_callback, NULL);
453 }
454 
455 /*
456  * sepgsql_get_label
457  *
458  * It returns a security context of the specified database object.
459  * If unlabeled or incorrectly labeled, the system "unlabeled" label
460  * shall be returned.
461  */
462 char *
sepgsql_get_label(Oid classId,Oid objectId,int32 subId)463 sepgsql_get_label(Oid classId, Oid objectId, int32 subId)
464 {
465 	ObjectAddress object;
466 	char	   *label;
467 
468 	object.classId = classId;
469 	object.objectId = objectId;
470 	object.objectSubId = subId;
471 
472 	label = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG);
473 	if (!label || security_check_context_raw((security_context_t) label))
474 	{
475 		security_context_t unlabeled;
476 
477 		if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0)
478 			ereport(ERROR,
479 					(errcode(ERRCODE_INTERNAL_ERROR),
480 			   errmsg("SELinux: failed to get initial security label: %m")));
481 		PG_TRY();
482 		{
483 			label = pstrdup(unlabeled);
484 		}
485 		PG_CATCH();
486 		{
487 			freecon(unlabeled);
488 			PG_RE_THROW();
489 		}
490 		PG_END_TRY();
491 
492 		freecon(unlabeled);
493 	}
494 	return label;
495 }
496 
497 /*
498  * sepgsql_object_relabel
499  *
500  * An entrypoint of SECURITY LABEL statement
501  */
502 void
sepgsql_object_relabel(const ObjectAddress * object,const char * seclabel)503 sepgsql_object_relabel(const ObjectAddress *object, const char *seclabel)
504 {
505 	/*
506 	 * validate format of the supplied security label, if it is security
507 	 * context of selinux.
508 	 */
509 	if (seclabel &&
510 		security_check_context_raw((security_context_t) seclabel) < 0)
511 		ereport(ERROR,
512 				(errcode(ERRCODE_INVALID_NAME),
513 			   errmsg("SELinux: invalid security label: \"%s\"", seclabel)));
514 
515 	/*
516 	 * Do actual permission checks for each object classes
517 	 */
518 	switch (object->classId)
519 	{
520 		case DatabaseRelationId:
521 			sepgsql_database_relabel(object->objectId, seclabel);
522 			break;
523 
524 		case NamespaceRelationId:
525 			sepgsql_schema_relabel(object->objectId, seclabel);
526 			break;
527 
528 		case RelationRelationId:
529 			if (object->objectSubId == 0)
530 				sepgsql_relation_relabel(object->objectId,
531 										 seclabel);
532 			else
533 				sepgsql_attribute_relabel(object->objectId,
534 										  object->objectSubId,
535 										  seclabel);
536 			break;
537 
538 		case ProcedureRelationId:
539 			sepgsql_proc_relabel(object->objectId, seclabel);
540 			break;
541 
542 		default:
543 			ereport(ERROR,
544 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
545 					 errmsg("sepgsql provider does not support labels on %s",
546 							getObjectTypeDescription(object))));
547 			break;
548 	}
549 }
550 
551 /*
552  * TEXT sepgsql_getcon(VOID)
553  *
554  * It returns the security label of the client.
555  */
556 PG_FUNCTION_INFO_V1(sepgsql_getcon);
557 Datum
sepgsql_getcon(PG_FUNCTION_ARGS)558 sepgsql_getcon(PG_FUNCTION_ARGS)
559 {
560 	char	   *client_label;
561 
562 	if (!sepgsql_is_enabled())
563 		PG_RETURN_NULL();
564 
565 	client_label = sepgsql_get_client_label();
566 
567 	PG_RETURN_TEXT_P(cstring_to_text(client_label));
568 }
569 
570 /*
571  * BOOL sepgsql_setcon(TEXT)
572  *
573  * It switches the security label of the client.
574  */
575 PG_FUNCTION_INFO_V1(sepgsql_setcon);
576 Datum
sepgsql_setcon(PG_FUNCTION_ARGS)577 sepgsql_setcon(PG_FUNCTION_ARGS)
578 {
579 	const char *new_label;
580 
581 	if (PG_ARGISNULL(0))
582 		new_label = NULL;
583 	else
584 		new_label = TextDatumGetCString(PG_GETARG_DATUM(0));
585 
586 	sepgsql_set_client_label(new_label);
587 
588 	PG_RETURN_BOOL(true);
589 }
590 
591 /*
592  * TEXT sepgsql_mcstrans_in(TEXT)
593  *
594  * It translate the given qualified MLS/MCS range into raw format
595  * when mcstrans daemon is working.
596  */
597 PG_FUNCTION_INFO_V1(sepgsql_mcstrans_in);
598 Datum
sepgsql_mcstrans_in(PG_FUNCTION_ARGS)599 sepgsql_mcstrans_in(PG_FUNCTION_ARGS)
600 {
601 	text	   *label = PG_GETARG_TEXT_P(0);
602 	char	   *raw_label;
603 	char	   *result;
604 
605 	if (!sepgsql_is_enabled())
606 		ereport(ERROR,
607 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
608 				 errmsg("sepgsql is not enabled")));
609 
610 	if (selinux_trans_to_raw_context(text_to_cstring(label),
611 									 &raw_label) < 0)
612 		ereport(ERROR,
613 				(errcode(ERRCODE_INTERNAL_ERROR),
614 				 errmsg("SELinux: could not translate security label: %m")));
615 
616 	PG_TRY();
617 	{
618 		result = pstrdup(raw_label);
619 	}
620 	PG_CATCH();
621 	{
622 		freecon(raw_label);
623 		PG_RE_THROW();
624 	}
625 	PG_END_TRY();
626 	freecon(raw_label);
627 
628 	PG_RETURN_TEXT_P(cstring_to_text(result));
629 }
630 
631 /*
632  * TEXT sepgsql_mcstrans_out(TEXT)
633  *
634  * It translate the given raw MLS/MCS range into qualified format
635  * when mcstrans daemon is working.
636  */
637 PG_FUNCTION_INFO_V1(sepgsql_mcstrans_out);
638 Datum
sepgsql_mcstrans_out(PG_FUNCTION_ARGS)639 sepgsql_mcstrans_out(PG_FUNCTION_ARGS)
640 {
641 	text	   *label = PG_GETARG_TEXT_P(0);
642 	char	   *qual_label;
643 	char	   *result;
644 
645 	if (!sepgsql_is_enabled())
646 		ereport(ERROR,
647 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
648 				 errmsg("sepgsql is not currently enabled")));
649 
650 	if (selinux_raw_to_trans_context(text_to_cstring(label),
651 									 &qual_label) < 0)
652 		ereport(ERROR,
653 				(errcode(ERRCODE_INTERNAL_ERROR),
654 				 errmsg("SELinux: could not translate security label: %m")));
655 
656 	PG_TRY();
657 	{
658 		result = pstrdup(qual_label);
659 	}
660 	PG_CATCH();
661 	{
662 		freecon(qual_label);
663 		PG_RE_THROW();
664 	}
665 	PG_END_TRY();
666 	freecon(qual_label);
667 
668 	PG_RETURN_TEXT_P(cstring_to_text(result));
669 }
670 
671 /*
672  * quote_object_names
673  *
674  * It tries to quote the supplied identifiers
675  */
676 static char *
quote_object_name(const char * src1,const char * src2,const char * src3,const char * src4)677 quote_object_name(const char *src1, const char *src2,
678 				  const char *src3, const char *src4)
679 {
680 	StringInfoData result;
681 	const char *temp;
682 
683 	initStringInfo(&result);
684 
685 	if (src1)
686 	{
687 		temp = quote_identifier(src1);
688 		appendStringInfo(&result, "%s", temp);
689 		if (src1 != temp)
690 			pfree((void *) temp);
691 	}
692 	if (src2)
693 	{
694 		temp = quote_identifier(src2);
695 		appendStringInfo(&result, ".%s", temp);
696 		if (src2 != temp)
697 			pfree((void *) temp);
698 	}
699 	if (src3)
700 	{
701 		temp = quote_identifier(src3);
702 		appendStringInfo(&result, ".%s", temp);
703 		if (src3 != temp)
704 			pfree((void *) temp);
705 	}
706 	if (src4)
707 	{
708 		temp = quote_identifier(src4);
709 		appendStringInfo(&result, ".%s", temp);
710 		if (src4 != temp)
711 			pfree((void *) temp);
712 	}
713 	return result.data;
714 }
715 
716 /*
717  * exec_object_restorecon
718  *
719  * This routine is a helper called by sepgsql_restorecon; it set up
720  * initial security labels of database objects within the supplied
721  * catalog OID.
722  */
723 static void
exec_object_restorecon(struct selabel_handle * sehnd,Oid catalogId)724 exec_object_restorecon(struct selabel_handle * sehnd, Oid catalogId)
725 {
726 	Relation	rel;
727 	SysScanDesc sscan;
728 	HeapTuple	tuple;
729 	char	   *database_name = get_database_name(MyDatabaseId);
730 	char	   *namespace_name;
731 	Oid			namespace_id;
732 	char	   *relation_name;
733 
734 	/*
735 	 * Open the target catalog. We don't want to allow writable accesses by
736 	 * other session during initial labeling.
737 	 */
738 	rel = heap_open(catalogId, AccessShareLock);
739 
740 	sscan = systable_beginscan(rel, InvalidOid, false,
741 							   NULL, 0, NULL);
742 	while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
743 	{
744 		Form_pg_database datForm;
745 		Form_pg_namespace nspForm;
746 		Form_pg_class relForm;
747 		Form_pg_attribute attForm;
748 		Form_pg_proc proForm;
749 		char	   *objname;
750 		int			objtype = 1234;
751 		ObjectAddress object;
752 		security_context_t context;
753 
754 		/*
755 		 * The way to determine object name depends on object classes. So, any
756 		 * branches set up `objtype', `objname' and `object' here.
757 		 */
758 		switch (catalogId)
759 		{
760 			case DatabaseRelationId:
761 				datForm = (Form_pg_database) GETSTRUCT(tuple);
762 
763 				objtype = SELABEL_DB_DATABASE;
764 
765 				objname = quote_object_name(NameStr(datForm->datname),
766 											NULL, NULL, NULL);
767 
768 				object.classId = DatabaseRelationId;
769 				object.objectId = HeapTupleGetOid(tuple);
770 				object.objectSubId = 0;
771 				break;
772 
773 			case NamespaceRelationId:
774 				nspForm = (Form_pg_namespace) GETSTRUCT(tuple);
775 
776 				objtype = SELABEL_DB_SCHEMA;
777 
778 				objname = quote_object_name(database_name,
779 											NameStr(nspForm->nspname),
780 											NULL, NULL);
781 
782 				object.classId = NamespaceRelationId;
783 				object.objectId = HeapTupleGetOid(tuple);
784 				object.objectSubId = 0;
785 				break;
786 
787 			case RelationRelationId:
788 				relForm = (Form_pg_class) GETSTRUCT(tuple);
789 
790 				if (relForm->relkind == RELKIND_RELATION)
791 					objtype = SELABEL_DB_TABLE;
792 				else if (relForm->relkind == RELKIND_SEQUENCE)
793 					objtype = SELABEL_DB_SEQUENCE;
794 				else if (relForm->relkind == RELKIND_VIEW)
795 					objtype = SELABEL_DB_VIEW;
796 				else
797 					continue;	/* no need to assign security label */
798 
799 				namespace_name = get_namespace_name(relForm->relnamespace);
800 				objname = quote_object_name(database_name,
801 											namespace_name,
802 											NameStr(relForm->relname),
803 											NULL);
804 				pfree(namespace_name);
805 
806 				object.classId = RelationRelationId;
807 				object.objectId = HeapTupleGetOid(tuple);
808 				object.objectSubId = 0;
809 				break;
810 
811 			case AttributeRelationId:
812 				attForm = (Form_pg_attribute) GETSTRUCT(tuple);
813 
814 				if (get_rel_relkind(attForm->attrelid) != RELKIND_RELATION)
815 					continue;	/* no need to assign security label */
816 
817 				objtype = SELABEL_DB_COLUMN;
818 
819 				namespace_id = get_rel_namespace(attForm->attrelid);
820 				namespace_name = get_namespace_name(namespace_id);
821 				relation_name = get_rel_name(attForm->attrelid);
822 				objname = quote_object_name(database_name,
823 											namespace_name,
824 											relation_name,
825 											NameStr(attForm->attname));
826 				pfree(namespace_name);
827 				pfree(relation_name);
828 
829 				object.classId = RelationRelationId;
830 				object.objectId = attForm->attrelid;
831 				object.objectSubId = attForm->attnum;
832 				break;
833 
834 			case ProcedureRelationId:
835 				proForm = (Form_pg_proc) GETSTRUCT(tuple);
836 
837 				objtype = SELABEL_DB_PROCEDURE;
838 
839 				namespace_name = get_namespace_name(proForm->pronamespace);
840 				objname = quote_object_name(database_name,
841 											namespace_name,
842 											NameStr(proForm->proname),
843 											NULL);
844 				pfree(namespace_name);
845 
846 				object.classId = ProcedureRelationId;
847 				object.objectId = HeapTupleGetOid(tuple);
848 				object.objectSubId = 0;
849 				break;
850 
851 			default:
852 				elog(ERROR, "unexpected catalog id: %u", catalogId);
853 				objname = NULL; /* for compiler quiet */
854 				break;
855 		}
856 
857 		if (selabel_lookup_raw(sehnd, &context, objname, objtype) == 0)
858 		{
859 			PG_TRY();
860 			{
861 				/*
862 				 * Check SELinux permission to relabel the fetched object,
863 				 * then do the actual relabeling.
864 				 */
865 				sepgsql_object_relabel(&object, context);
866 
867 				SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, context);
868 			}
869 			PG_CATCH();
870 			{
871 				freecon(context);
872 				PG_RE_THROW();
873 			}
874 			PG_END_TRY();
875 			freecon(context);
876 		}
877 		else if (errno == ENOENT)
878 			ereport(WARNING,
879 					(errmsg("SELinux: no initial label assigned for %s (type=%d), skipping",
880 							objname, objtype)));
881 		else
882 			ereport(ERROR,
883 					(errcode(ERRCODE_INTERNAL_ERROR),
884 					 errmsg("SELinux: could not determine initial security label for %s (type=%d): %m", objname, objtype)));
885 
886 		pfree(objname);
887 	}
888 	systable_endscan(sscan);
889 
890 	heap_close(rel, NoLock);
891 }
892 
893 /*
894  * BOOL sepgsql_restorecon(TEXT specfile)
895  *
896  * This function tries to assign initial security labels on all the object
897  * within the current database, according to the system setting.
898  * It is typically invoked by sepgsql-install script just after initdb, to
899  * assign initial security labels.
900  *
901  * If @specfile is not NULL, it uses explicitly specified specfile, instead
902  * of the system default.
903  */
904 PG_FUNCTION_INFO_V1(sepgsql_restorecon);
905 Datum
sepgsql_restorecon(PG_FUNCTION_ARGS)906 sepgsql_restorecon(PG_FUNCTION_ARGS)
907 {
908 	struct selabel_handle *sehnd;
909 	struct selinux_opt seopts;
910 
911 	/*
912 	 * SELinux has to be enabled on the running platform.
913 	 */
914 	if (!sepgsql_is_enabled())
915 		ereport(ERROR,
916 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
917 				 errmsg("sepgsql is not currently enabled")));
918 
919 	/*
920 	 * Check DAC permission. Only superuser can set up initial security
921 	 * labels, like root-user in filesystems
922 	 */
923 	if (!superuser())
924 		ereport(ERROR,
925 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
926 		  errmsg("SELinux: must be superuser to restore initial contexts")));
927 
928 	/*
929 	 * Open selabel_lookup(3) stuff. It provides a set of mapping between an
930 	 * initial security label and object class/name due to the system setting.
931 	 */
932 	if (PG_ARGISNULL(0))
933 	{
934 		seopts.type = SELABEL_OPT_UNUSED;
935 		seopts.value = NULL;
936 	}
937 	else
938 	{
939 		seopts.type = SELABEL_OPT_PATH;
940 		seopts.value = TextDatumGetCString(PG_GETARG_DATUM(0));
941 	}
942 	sehnd = selabel_open(SELABEL_CTX_DB, &seopts, 1);
943 	if (!sehnd)
944 		ereport(ERROR,
945 				(errcode(ERRCODE_INTERNAL_ERROR),
946 			   errmsg("SELinux: failed to initialize labeling handle: %m")));
947 	PG_TRY();
948 	{
949 		exec_object_restorecon(sehnd, DatabaseRelationId);
950 		exec_object_restorecon(sehnd, NamespaceRelationId);
951 		exec_object_restorecon(sehnd, RelationRelationId);
952 		exec_object_restorecon(sehnd, AttributeRelationId);
953 		exec_object_restorecon(sehnd, ProcedureRelationId);
954 	}
955 	PG_CATCH();
956 	{
957 		selabel_close(sehnd);
958 		PG_RE_THROW();
959 	}
960 	PG_END_TRY();
961 
962 	selabel_close(sehnd);
963 
964 	PG_RETURN_BOOL(true);
965 }
966